Bekijk online

advertisement
Faculteit Toegepaste Wetenschappen
Vakgroep Informatietechnologie (INTEC)
Voorzitter: Prof. dr. ir. P. Lagasse
Ontwerp van de besturingssoftware
voor een Vector Netwerk Analyser
door Wim Heirman
Promotor: Prof. dr. ir. J. Vandewege
Thesisbegeleiders: ir. J. Pletinckx en ir. K. Van Renterghem
Afstudeerwerk ingediend tot het behalen van de academische graad van
Burgerlijk Ingenieur in de Computerwetenschappen,
optie Computerarchitectuur
Academiejaar 2002–2003
Toelating tot bruikleen
De auteur geeft de toelating dit afstudeerwerk voor consultatie beschikbaar te stellen
en delen van het afstudeerwerk te copiëren voor persoonlijk gebruik. Elk ander gebruik
valt onder de beperkingen van het auteursrecht, in het bijzonder met betrekking tot de
verplichting de bron uitdrukkelijk te vermelden bij het aanhalen van resultaten uit dit
afstudeerwerk.
Wim Heirman
1 juni 2003
i
Dankwoord
Graag zou ik iedereen willen bedanken die heeft bijgedragen tot de verwezenlijking van
dit eindwerk. In het bijzonder dank ik:
• Prof. Vandewege voor het organiseren van dit thesisproject en het beschikbaar stellen
van de nodige apparatuur,
• mijn thesisbegeleiders Jo Pletinckx en Koen Van Renterghem voor het eeuwige enthousiasme waarmee ze mijn niet aflatende stroom vragen steeds beantwoordden,
• de mensen op de mailinglijst linux-usb-devel voor hun snelle en accurate antwoorden m.b.t. de USB ondersteuning in de Linux kernel,
• mijn thesiscollega’s Carl, Karel, Pascal, Vik en Wim,
• Dr. Rik Van Daele voor het taalkundig advies,
• Donald Knuth voor TEX, zodat mijn figuren op hun plaats blijven staan.
ii
Ontwerp van de besturingssoftware voor een
Vector Netwerk Analyser
door
Wim Heirman
Afstudeerwerk ingediend tot het behalen van de academische graad van
Burgerlijk Ingenieur in de Computerwetenschappen, optie Computerarchitectuur
Academiejaar 2002–2003
Universiteit Gent
Faculteit Toegepaste Wetenschappen
Vakgroep Informatietechnologie (INTEC)
Voorzitter : Prof. dr. ir. P. Lagasse
Promotor : Prof. dr. ir. J. Vandewege
Thesisbegeleiders : ir. J. Pletinckx en ir. K. Van Renterghem
Samenvatting
Deze thesis is een deel van een project dat gerealiseerd werd door vier groepen thesisstudenten bij INTEC design. Het project heeft als doel een Vector Netwerk Analyser (VNA)
te bouwen die via een netwerk door een computer kan worden aangestuurd. In dit afstudeerwerk werd de besturingssoftware van de VNA ontwikkeld. Een eerste implementatie
bevatte een DSP-processor en bouwde voort op een reeds ontwikkelde TCP/IP stack voor
dit platform. Nadien werd een ander mogelijk platform bekeken dat onder een Linux kernel
draaide en met een USB interface verbonden was met de rest van het toestel. De verschillende lagen voor communicatie tussen de hardware van de VNA en de gebruikersinterface op
de via het netwerk verbonden computer werden uitgewerkt en geı̈mplementeerd. Speciale
aandacht werd besteed aan het optimaliseren van de doorvoersnelheid en betrouwbaarheid
van de USB verbinding en het implementeren van een algoritme voor flow-control, zodat
het toestel met verschillende types netwerken kan samenwerken.
In dit afstudeerwerk wordt, na een overzicht te hebben gegeven van de verschillende
technologieën die werden gebruikt, elke laag afzonderlijk belicht. Een apart hoofdstuk
behandelt de eigenschappen van een geschikte gebruikersinterface. Tot slot worden enkele
mogelijkheden tot verdere uitbreiding besproken.
Trefwoorden: VNA, DSP, Linux, USB
iii
Inhoudsopgave
1 Inleiding
1.1 Een RF Vector Netwerk Analyser met Internet besturing . . . . . . . . . . .
1.2 Implementatie met behulp van een DSP . . . . . . . . . . . . . . . . . . . .
1.3 Implementatie met Linux en USB . . . . . . . . . . . . . . . . . . . . . . . .
2 Implementatie met behulp van een DSP
2.1 De Hardware . . . . . . . . . . . . . . . . . . . . . .
2.1.1 De ADSP-21065L SHARC DSP . . . . . . . .
2.1.2 De Cirrus Logic CS8900A ethernetcontroller .
2.2 Analog Devices’ VisualDSP Development Kit . . . .
2.3 Porten van de TCP/IP stack . . . . . . . . . . . . .
2.3.1 Conversie naar VDK threads . . . . . . . . .
2.3.2 Edge- vs. level-triggered interrupts . . . . . .
2.3.3 Big of little endian . . . . . . . . . . . . . . .
2.3.4 Lijngebufferde sockets . . . . . . . . . . . . .
2.4 Uitbreidingen op basis van de TCP/IP stack . . . .
2.4.1 Het boot-proces . . . . . . . . . . . . . . . .
2.4.2 Firmware-update . . . . . . . . . . . . . . . .
2.4.3 Bestandssysteem in Flash . . . . . . . . . . .
2.5 Besluit . . . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
3
3
5
5
5
7
8
9
9
10
10
11
12
12
13
15
16
3 Implementatie met Linux en USB
17
3.1 Inleiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.2 Structuur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
4 Het
4.1
4.2
4.3
4.4
USB Client-device
De USB Interface . . . . . . .
Cypress FX2 . . . . . . . . .
Firmware voor de FX2 . . . .
Programmeren van de FPGA
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5 USB in Linux
5.1 Het Linux USB subsysteem . . . . . . . . .
5.2 Een USB Driver . . . . . . . . . . . . . . .
5.3 Performantie . . . . . . . . . . . . . . . . .
5.3.1 Structuur van het VIA moederbord
5.3.2 Verwerking van gegevens in bulk . .
iv
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
21
23
25
27
.
.
.
.
.
31
31
33
38
38
40
5.3.3
5.3.4
Verwerking van gegevens met real-time eisen . . . . . . . . . . . . . 43
Uitlezen van gegevens met beperkte latentie . . . . . . . . . . . . . . 46
6 LibVNA
48
6.1 Inleiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.2 Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
7 WebVNA
7.1 Inlezen van samples . . . . . . . . . . . .
7.2 Locking . . . . . . . . . . . . . . . . . . .
7.3 Besturing van de hardware . . . . . . . .
7.4 Interactie met de gebruiker . . . . . . . .
7.5 Overdracht van samples naar de gebruiker
7.5.1 Streaming van data in real-time . .
7.5.2 Bulk transfer over TCP . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
51
51
52
52
53
53
53
59
8 Het gebruikersprogramma
61
8.1 Een client in Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
8.2 Andere mogelijkheiden van de webserver . . . . . . . . . . . . . . . . . . . . 63
9 De
9.1
9.2
9.3
9.4
toekomst
IPv6 . . . . . . .
Beveiliging . . .
Kalibratie . . . .
Eliminatie van de
. . . . . . .
. . . . . . .
. . . . . . .
harde schijf
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
64
64
64
65
65
10 Conclusie
66
10.1 DSP platform . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
10.2 Linux en USB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
10.3 Besluit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
A Interface van LibVNA
68
Bibliografie
71
v
Lijst van afkortingen
API
Application Programming Interface
DCCP
DSP
Datagram Congestion Control Protocol
Digital Signal Processor
EHCI
Enhanced HCI
FPGA
Field Programmable Gate Array
HCI
HTTP
Host Controller Interface
Hyper Text Transfer Protocol
ICE
IDE
IP
ISR
ISQ
In Circuit Emulator
Integrated Development Environment
Internet Protocol
Interrupt Service Routine
Interrupt Status Queue
OHCI
Open HCI
PID
Product IDentification
TCP
TFRC
Transmission Control Protocol
TCP Friendly Rate Control
UDP
UHCI
URB
USART
USB
User Datagram Protocol
Universal HCI
USB Request Block
Universal Synchronous and Asynchronous Receiver/Transmitter
Universal Serial Bus
VDK
VID
VNA
VisualDSP Development Kit
Vendor IDentification
Vector Netwerk Analyser
vi
“Een RF VECTOR NETWERK ANALYSER
met INTERNET besturing.”
Doel van dit thesisproject is het ontwerp van een RF Vector Netwerk Analyser (VNA)
Hoofdstuk
1
die via een WEB browser over het Internet kan worden bestuurd, en die ondanks de
hoge performantie gebruiksvriendelijk en prijsgunstig is.
Dit is een uitstekende gelegenheid om ontwerpervaring op te doen. Er is geen
Inleiding
bijzondere voorkennis vereist. Je krijgt toegang tot krachtige meetapparatuur en
ontwerpsoftware. En je gebruikt zeer recente componenten, zoals opamps met
honderden MHz bandbreedte, hoogperformante AD convertoren, een krachtige DSP
processor, PLL chips, een Directe Digitale Synthesiser (DDS chip), en snelle Field
1.1
Een RF
Vector
Netwerk
Analyser
Internet vrij te
Programmable
Gate
Array (FPGA)
componenten
metmet
honderdduizenden
programmeren
gates.
besturing
De software wordt ontwikkeld in verschillende lagen en talen: VHDL (FPGA logica),
Een
Vector
Netwerk
Analyser
een meettoestel
dat de
frequentierespons
van een
C/C++
(DSP
processor),
C++(VNA)
(WEBis software)
en JAVA
(applets).
Hier zijn TCP/IP
stack,
http
daemon,
gebruikersinterface
model
en
applets
de
sleutelwoorden.
lineair analoog netwerk (bv. een filter of niet overstuurde versterker) opmeet. Hiertoe
wordt een signaal met gekende frequentie, amplitude en fase in het netwerk geı̈njecteerd.
Je bepaalt mee hoe het toestel groeit vanuit rudimentaire beginspecificaties, hoe
tussen teams wordt gediscuteerd en onderhandeld, en hoe belangrijke
signaal terug opgemeten zodat amplitude- en faserespons van het netwerk bepaald kunnen
systeembeslissingen tot stand komen. Je steekt veel op van de andere teams, en leert
worden.
gebeurt begrijpen.
voor verschillende frequentiepunten zodat een compleet beeld van de
ook hunDit
vakjargon
Aan de uitgang (of de ingang indien men de reflectiekarakteristiek wil kennen) wordt het
karakteristiek van het netwerk opgemeten kan worden.
Traditioneel
is zo’n
opgebouwd als
eenexperimentele
aantal knoppen
voor
In december
2002
gaanVNA
we proefdraaien
metéén
eentoestel
eerste met
versie
boards.
In besturing
de lente envan
we alles
volwaardige
VNA,
en
de
een 2003
CRT ofvoegen
LCD scherm
voor samen
weergavetotvaneen
de resultaten.
In dit
project
demonstreren
we de gebruikersinterface
besturing vanuit een
WEB
browser.
Hiervoor
kunnen
op korte
zullen
we deze fysische
echter
vervangen
door
een virtuele.
De gebruiker
tijd
professionele
meerlagenprinten
worden
aangemaakt.
Is
de
performantie
goed,
dan
zit aan zijn PC – die zich eventueel zeer ver van de VNA zelf en dus van het te meten
bouwen we een pilootreeks voor gebruik in het labo, bij practica of voor industriële
netwerk kan bevinden – en maakt over het Internet verbinding met de VNA. Deze bevat een
toepassingen die een “low cost” instrument vereisen.
webserver zodat de gebruiker het benodigde programma, dat als Java-applet is uitgevoerd,
Figuur 1.1: De structuur van de VNA.
Aard: praktisch
Inlichtingen: Prof. J. Vandewege
Discipline: Elektronisch Ontwerp en Systemen
1
van het toestel zelf kan downloaden. Op die manier moet op de PC van de gebruiker geen
software geı̈nstalleerd worden; een webbrowser en Java virtuele machine volstaan.
Het ontwerp van een VNA beslaat een aantal domeinen, gaande van analoog en digitaal
elektronisch ontwerp tot het programmeren van een hoog niveau gebruikersinterface. Het
project, opgezet door het INTEC design labo, is dan ook opgesplitst in vier verschillende
thesisonderwerpen:
• “Ontwerp van een VNA S-parameter testset”, door Carl Lylon en Vik Van Hoye.
• “Ontwerp van een VNA ontvanger”, door Karel Pauwels.
• “Ontwerp van de firmware van een VNA”, door Pascal Lotiquet en Wim Pletinckx.
• “Ontwerp van de besturingssoftware voor een VNA”, door Wim Heirman.
Deze werden alle uitgevoerd tijdens het academiejaar 2002-2003. Naast het eigenlijke
ontwerpen hebben we dus ook ervaring kunnen opdoen met het werken in teamverband,
wat aan deze thesis een additionele dimensie gaf. Enkel de zender (TX) werd reeds vroeger
in het kader van een doctoraatsthesis [Lam00] gemaakt.
Het eerste onderwerp [LH03] omvat de verbinding tussen de VNA en het te meten
netwerk. Deze bestaat uit een aantal versterkers/verzwakkers, vermogenssplitsers en directionele koppelaars, waarvan er enkele stuurbaar zijn door de gebruiker. De ontvanger
omvat het tweede deel [Pau03]. Hier wordt het signaal dat door het te meten netwerk
gegaan is, en een frequentie heeft tussen 100 MHz en 1 GHz, met behulp van een mixer en
een lokale oscillator omgezet naar 800 kHz. Dit gebeurt echter zó dat amplitude en fase
van dit signaal verwant zijn met die van het originele signaal. In het derde deel van dit
project wordt het signaal van 800 kHz samen met een referentiesignaal bemonsterd door
16 bit Σ∆ A/D convertoren. De gedigitaliseerde signalen worden in een FPGA verwerkt
en vergeleken, met als resultaat de amplitude en fase van het meetsignaal [LP03]. Het laatste onderdeel van het project wordt behandeld in dit afstudeerwerk. Een controleorgaan
geeft opdrachten van de gebruiker door aan de FPGA en de andere controleerbare componenten, en ontvangt en verwerkt de meetresultaten van de FPGA voor weergave door de
gebruikersinterface. Deze laatste, die normaal in Java uitgevoerd zou worden, werd niet
volledig uitgewerkt. Er werd echter wel een proof of concept gemaakt die de werking van
de rest van het toestel kan demonstreren.
In het eerste deel van dit afstudeerwerk wordt een eerder ontwikkeld DSP-bord als
besturingsprocessor gebruikt. Hiervoor was reeds een TCP/IP-stack ontwikkeld door Bart
Meerschman en Koen Van Renterghem als een vorig afstudeerwerk [MR02].
In het tweede deel wordt een andere mogelijkheid bekeken, namelijk een aansturing
via USB 2.0. Op de VNA zit een USB tranceiver van het type Cypress FX2; deze bevat
een geı̈ntegreerde microcontroller uit de 8051 familie. De VNA kan rechtstreeks met de PC
2
van de gebruiker verbonden worden, of geı̈ntegreerd worden in een compacte PC waarop
een webserver draait. Zo kan het toestel over het Internet bestuurd worden.
1.2
Implementatie met behulp van een DSP
Het platform voor de eerste implementatie bestaat uit een dual-DSP bord, reeds eerder
ontwikkeld door het INTEC design labo. Dit PCB bevat twee DSP’s van Analog Devices,
een FPGA, een ethernetaansluiting en een hoeveelheid SDRAM en Flash geheugen. Voor
de VNA wordt slechts één DSP gebruikt. Vorig jaar werd een TCP/IP-stack ontwikkeld
voor dit bord in het kader van het toenmalige thesisproject. Deze stack gebruikt een zelf
ontwikkeld prioriteitsgebaseerd besturingssysteem.
Sinds de tweede release van VisualDSP, de design tool van Analog Devices voor de
SHARC DSP’s, heeft de SHARC zijn eigen besturingssysteem met meer mogelijkheden
dan de zelfgeschreven kernel. Er werd dus begonnen met het porteren van de bestaande
TCP/IP implementatie naar dit nieuwe besturingssyteem, de VisualDSP Kernel (VDK).
Vervolgens werden enkele componenten verder uitgewerkt die nodig zouden zijn in de
latere VNA, namelijk een bestandssysteem in Flash-geheugen voor de webserver en een
systeem om firmware-updates te doen over het Internet.
Een beschrijving van de mogelijkheden van de VDK wordt gegeven in paragraaf 2.2.
Een overzicht van de benodigde aanpassingen aan de TCP/IP-stack is opgenomen in paragraaf 2.3. Enkele toepassingen gemaakt op basis van de TCP/IP-stack worden beschreven
in paragraaf 2.4.
1.3
Implementatie met Linux en USB
Een tweede implementatie maakt gebruik van een PC-compatibel moederbord waarop een
standaard Linux kernel draait. Een USB 2.0 aansluiting wordt gebruikt om gegevens in
en uit te voeren. De aanwezigheid van een robuuste TCP/IP-stack, geı̈ntegreerd in de
Linux kernel, en de beschikbaarheid van een uitgebreide collectie netwerksoftware (o.m.
webservers, DHCP-clients, . . . ) voor het Linux-platform zorgen voor een solide basis.
Er werd gekozen voor een moederbord uit VIA’s EPIA-M reeks. Deze PCB’s in miniITX formaat (17 bij 17 cm) bevatten een low-power VIA C3 processor en zijn dus ideaal
voor embedded toepassingen. Hierop draait een standaard Linux distributie, nl. SuSE
Linux 8.1. Aan de andere kant van de USB-aansluiting vinden we een FX2 van Cypress.
Dit is een volledige USB tranceiver met een geı̈ntegreerde microcontroller.
Enerzijds werd de geschikte besturingssoftware voor de FX2 geschreven, en anderzijds
de nodige programmatuur voor het VIA-moederbord. Deze laatste bestaat uit een kernelmode driver, een bibliotheek met enkele low-level functies en een besturingsprogramma
dat opdrachten van de gebruikersinterface doorstuurt naar de VNA en de opgemeten data
3
in de andere richting verstuurt. De volledige beschrijving hiervan begint met een uitgebreidere decompositie in hoofdstuk 3, de afzonderlijke componenten worden besproken in
hoofdstukken 4 tot en met 7. Daarna volgen nog hoofdstuk 8, dat de benodigde functionaliteit van de gebruikersinterface beschrijft en hoofdstuk 9 dat een overzicht geeft van
een aantal mogelijke uitbreidingen. In hoofdstuk 10 tenslotte worden de voor- en nadelen
van beide platforms naast elkaar gezet.
4
Hoofdstuk 2
Implementatie met behulp van een
DSP
2.1
De Hardware
De eerste implementatie gaat uit van het dual-DSP bord dat vorig jaar is ontwikkeld
bij INTEC design. Dit bord is opgebouwd rond twee DSP’s van het type ADSP-21065L,
het instapmodel van de SHARC reeks van Analog Devices. Een FPGA staat in voor de
verbinding tussen beide DSP’s en de overige hardware. Andere componenten zijn een
CS8900A ethernetcontroller van Cirrus Logic en voor elk van de DSP’s 16 MiB1 SDRAM
werkgeheugen en 1 MiB Flash programmageheugen.
Hieronder volgt een kort overzicht van de belangrijkste eigenschappen van enkele componenten. Een volledige beschrijving is uiteraard te vinden in de datasheets.
2.1.1
De ADSP-21065L SHARC DSP
De architectuur van deze DSP is gebaseerd op de Harvard Architectuur, deze heeft gescheiden programma- en datageheugens die parallel toegankelijk zijn. De SHARC (Super
Harvard Architecture) voegt hier nog enkele componenten aan toe: een I/O-processor en
I/O-bus die zonder tussenkomst van de processor data tussen het geheugen en een randapparaat kunnen verplaatsen en een instructiecache die enkele reeds uitgevoerde instructies
bijhoudt. Het is immers mogelijk dat data (een constante) uit het programmageheugen
gehaald moet worden en tijdens deze cyclus kan uiteraard geen nieuwe instructie worden
opgehaald. De betreffende instructie wordt uitgelezen in de volgende cyclus en wordt tevens in de cache geschreven, zodat ze de volgende keer één cyclus vroeger toegankelijk
is.
1
In deze tekst worden de nieuwe benamingen voor binaire meervouden gebruikt, deze werden in december 1998 door het IEC gestandaardiseerd. Dus 1 MiB = 1024 KiB = 220 bytes, terwijl 1 MB = 1000 kB
= 106 bytes. B staat steeds voor byte, bit heeft geen afkorting.
5
Figuur 2.1: Het gebruikte dual-DSP bord.
Alle leden van de SHARC architectuur zijn ontworpen voor het verwerken van 32
bit gehele en 32 of 40 bit vlottende komma gegevens. De basisinstructie van de DSP, de
Multiply and Accumulate of MAC, is in één cyclus uitvoerbaar en is niet gepijplijnd. Het
resultaat is dus direct beschikbaar voor een volgende instructie. Tegelijk met een MAC
kunnen ook nog een gewone optelling en een shift uitgevoerd worden. Met een interne
klokfrequentie van 60 MHz bekomt men zo een maximale verwerkingssnelheid van 180
MFLOPS. Deze drie operaties worden in één instructiewoord van 48 bits gespecifieerd.
Andere voorzieningen zijn twee adresgeneratoren. Deze genereren het adres voor dataen instructiegeheugentoegangen en kunnen voor of na elke toegang het adres automatisch
incrementeren met een ingestelde waarde. Circulaire buffers worden eveneens in hardware
ondersteund. Bovendien kan een deel van het adres gespiegeld uitgevoerd worden. Dit is
zeer voordelig bij FFT ‘butterfly’ berekeningen. Ook lussen worden geı̈mplementeerd met
een in hardware uitgevoerde loop-counter. Er is bijgevolg geen nood aan een “decrementeer
teller en herhaal indien niet nul” instructie, dit gebeurt parallel met het uitvoeren van de
laatste instructie en vraagt dus geen extra klokcyclus. Tenslotte zorgt een aantal seriële
poorten voor communicatie met de buitenwereld.
De hier gebruikte DSP, de 21065L, is het instapmodel van de SHARC-reeks. Het ver-
6
KEY FEATURES
66 MIPS, 198 MFLOPS Peak, 132 MFLOPS Sustained
Performance
User-Configurable 544 Kbits On-Chip SRAM Memory
Two External Port, DMA Channels and Eight Serial
Port, DMA Channels
Parallel Computations
Single-Cycle Multiply and ALU Operations in Parallel with
Dual Memory Read/Writes and Instruction Fetch
Multiply with Add and Subtract for Accelerated FFT Butterfly Computation
1024-Point Complex FFT Benchmark: 0.274 ms (18,221
Cycles)
32 ⴛ 48 BIT
TWO INDEPENDENT
DUAL-PORTED BLOCKS
PROCESSOR PORT
ADDR
ADDR
DAG1
DAG2
8 ⴛ 4 ⴛ 32
8 ⴛ 4 ⴛ 24
DATA
DATA
I/O PORT
DATA
ADDR
ADDR
DATA
JTAG
BLOCK 1
INSTRUCTION
CACHE
BLOCK 0
DUAL-PORTED SRAM
CORE PROCESSOR
PROGRAM
SEQUENCER
24
PM ADDRESS BUS
32
DM ADDRESS BUS
IOD
48
IOA
17
7
TEST &
EMULATION
EXTERNAL
PORT
SDRAM
INTERFACE
ADDR BUS
MUX
24
MULTIPROCESSOR
INTERFACE
48 PM DATA BUS
BUS
CONNECT
(PX)
DATA BUS
MUX
40 DM DATA BUS
32
HOST PORT
DATA
REGISTER
FILE
MULTIPLIER
16 ⴛ 40 BIT
IOP
REGISTERS
DMA
CONTROLLER
4
(2 Rx, 2Tx)
(MEMORY MAPPED)
BARREL
SHIFTER
ALU
CONTROL,
STATUS, TIMER
&
DATA BUFFERS
SPORT 0
(I2S)
(2 Rx, 2Tx)
SPORT 1
(I2S)
I/O PROCESSOR
Figure 1. Functional Block Diagram
SHARC is a registered trademark of Analog Devices, Inc.
Figuur 2.2: Blokdiagram van de ADSP-21065L.
REV. B
Information
furnished
by Analog
Devices is believed
to be accurate
and vooral in de hoeveelheid intern geheugen. Hier
schil
tussen
de verschillende
modellen
ligt
reliable. However, no responsibility is assumed by Analog Devices for its
One Technology Way, P.O. Box 9106, Norwood, MA 02062-9106, U.S.A.
use, nor for any infringements of patents or other rights of third parties
is result
dit 544
Kibit,
Mibit.
Dit geheugen
is dual-ported,
d.w.z.
781/329-4700
World Wide
Web Site: http://www.analog.com
which may
from its
use. Noandere
license is versies
granted byhebben
implicationtot
or 4 Tel:
Fax: 781/326-8703
© Analog Devices, Inc., 2000
otherwise under any patent or patent rights of Analog Devices.
dat het tegelijk een instructiewoord en een datawoord kan leveren. Wanneer het interne
geheugen te klein is kan men zijn toevlucht nemen tot extern SDRAM geheugen. Een
geheugencontroller hiervoor is op de DSP geı̈ntegreerd. De toegangstijd hiervan is eveneens één klokcyclus; data- en instructiegeheugentoegangen kunnen echter niet tezelfdertijd
gebeuren. Bovendien wordt het externe geheugen aangesproken in 32 bit woorden. Een instructiewoord is 48 bit en zal dus twee 32 bit woorden in beslag nemen. Dit zorgt voor
een verdubbelde toegangstijd en een overhead aan geheugenruimte van 33 %.
2.1.2
De Cirrus Logic CS8900A ethernetcontroller
De CS8900A van Cirrus Logic is een geı̈ntegreerde 10Base-T Ethernet controller die met
een minimaal aantal externe componenten een complete ethernetinterface implementeert.
Het IC implementeert een ISA-interface met 16 bit databus. Om een pakket op het ethernet te verzenden volstaat het enkele registers in te stellen, waarna de inhoud van het pakket
naar het IC geschreven kan worden. Op de chip bevindt zich een hoeveelheid buffergeheugen dat men de pakketbuffers noemt. Deze zijn memory-mapped, het schrijven van een
pakket naar het IC lijkt voor de CPU daarom een gewone block-copy die meestal zeer
efficiënt uitgevoerd kan worden. De ISA-interface is opgevat als zijnde little endian. De
byte die op datalijnen D0 tot D7 aangebracht wordt zal dus als eerste verstuurd worden.
7
Wanneer een pakket via de ethernet-interface binnenkomt zal het IC eerst de bestemming controleren. Deze staat als een 48 bit ‘MAC-adres’ in de header van het pakket. Het
IC kan dus op basis van zijn eigen MAC-adres beslissen of het pakket al dan niet voor deze
interface bedoeld is. De systeemprocessor wordt dus niet lastig gevallen met ander verkeer op het ethernet. Broadcast-pakketten worden steeds aanvaard en in de ‘promiscious’
mode wordt alle verkeer geaccepteerd. Wanneer een geschikt pakket ontvangen wordt zal
de CS8900A zijn interruptlijn activeren. De processor kan dan de inhoud van het pakket
inlezen uit de pakketbuffers.
Een interrupt kan nog andere redenen hebben, bv. dat het vorige pakket verzonden
is en een nieuw verstuurd kan worden, of een foutconditie. Door een bepaald register, de
Interrupt Status Queue (ISQ), uit te lezen kan de processor te weten komen welke de
oorzaak van de interrupt was. Indien meerdere gebeurtenissen zich voordoen voordat de
processor tijd heeft om de interrupt te verwerken worden deze gequeued; door de ISQ
meerdere malen uit te lezen worden de verschillende oorzaken gevonden. De interrupt-pin
blijft actief totdat de laatste oorzaak opgevraagd werd.
2.2
Analog Devices’ VisualDSP Development Kit
De VisualDSP ontwerpsomgeving van Analog Devices combineert een C/C++ compiler,
een assembler en een debugger in een geı̈ntegreerde ontwerpsomgeving (IDE, Integrated
Development Environment). De gebruikersinterface is gelijkaardig aan die van vele IDE’s
voor desktop ontwikkeling zoals Microsoft’s Visual Studio. Een In Circuit Emulator (ICE)
laat toe om de inhoud van de registers van de DSP te bekijken wanneer het programma op
de werkelijke hardware draait, dit in tegenstelling tot een simulator die volledig in software
uitgevoerd is. Op deze manier zijn vele van de voordelen van desktop ontwikkeling naar
de embedded wereld gebracht.
In versie 2.0 van deze ontwerpsomgeving stelt Analog ook een eigen kernel beschikbaar:
de VisualDSP Kernel (VDK). Deze kernel laat toe om meerdere threads parallel te laten
uitvoeren. Op regelmatige tijdstippen (bv. om de 5 ms) wordt hiertoe een timer-interrupt
gegenereerd die de scheduler oproept. Deze zal beslissen welke de volgende thread is die
van de processor gebruik mag maken. Hiertoe heeft elke thread een statische prioriteit
gekregen. Bovendien houdt de scheduler bij welke threads effectief iets te doen hebben,
en zal dus vermijden een thread te starten die wacht op een andere thread of een I/O
apparaat. De scheduler kan op elk moment opgeroepen worden en een andere taak laten
lopen. Men weet dus niet op voorhand op welk punt een thread onderbroken kan worden.
Het is dus noodzakelijk geworden om op te letten voor race-condities, niet enkel tussen
threads en interruptroutines, maar ook tussen threads onderling. De eigen scheduler die
bij het project vorig jaar gebruikt werd had dit probleem niet: elke thread deed een vaste
hoeveelheid werk en besliste daarna zelf wanneer de scheduler opnieuw opgeroepen werd.
8
Men kon er dan ook voor zorgen dat op dit punt slechts een minimum aan variabelen
belangrijk was. Enkel deze moeten dan bewaard worden tussen twee uitvoeringen van de
thread. Een pre-emptieve scheduler moet immers steeds alle registers van de DSP op de
stack plaatsten bij het stoppen van één thread en alles terugplaatsen vóór het starten van
een volgende en zal daarom meer tijd nodig hebben om van thread te veranderen. Er moest
echter wel beslist worden hoeveel werk elke thread deed – niet teveel, want dan duurt het
te lang voor elke thread heeft kunnen uitvoeren. Bij te weinig werk zal de scheduler voor
teveel overhead zorgen – een pre-emptieve scheduler regelt dit automatisch.
Zoals gezegd zal de scheduler een thread enkel starten wanneer die ook iets te doen
heeft. Het principe dat hiervoor gebruikt wordt is de semafoor. Deze bestaat uit een geheel
getal waarop twee operaties gedefinieerd zijn: post en pend. pend zal controleren of het
getal niet nul is. Indien dit niet het geval is wordt het getal met één gedecrementeerd, in het
andere geval zal de thread die pend aanroept blokkeren en daarom niet meer gescheduled
worden. post zal de semafoor incrementeren, indien er een thread geblokeerd is op dezelfde
semafoor weet de scheduler dat deze terug uitvoerbaar is – tenzij meerdere threads op
dezelfde semafoor wachten, dan zal slechts één van hen kunnen verdergaan. Met semaforen
is dus een alternatief te maken voor busy waiting: de wachtende thread blokkeert op een
semafoor; wanneer aan de conditie die deze thread wakker moet maken is voldaan, wordt
de semafoor door een andere thread of een interruptroutine gepost. De scheduler heeft
nu inzicht in het al dan niet actief zijn van threads en kan de processor dus efficiënter
gebruiken.
2.3
2.3.1
Porten van de TCP/IP stack
Conversie naar VDK threads
De code van de bestaande TCP/IP stack is reeds onderverdeeld in een aantal functies die
door de scheduler opgeroepen worden. Deze werden omgevormd tot volwaardige threads.
De anderen zijn te beschouwen als hulproutines en draaien tijdens het tijdssegment van
een echte thread. Om een functie om te vormen tot een thread moet eerst in de VDK een
nieuwe thread gedefinieerd worden met een geschikte prioriteit, en gezorgd worden dat
deze op het juiste moment opgestart wordt. Verder zijn de bestaande routines geschreven
om één keer hun functie uit te voeren en daarna de functie te verlaten om terug te keren
naar de scheduler. Een thread heeft de bedoeling op zichzelf te staan en niet te beëindigen.
Er moet dus voor gezord worden dat de functie continu opgeroepen wordt. Bij de meeste
functies blijkt het mogelijk op een semafoor te blokkeren tussen twee uitvoeringen wanneer
geen werk te doen is. Voor andere functies blijkt het moeilijk een semafoor op het juiste
moment te posten. Hier kan een periodische semafoor uitweg bieden. Dit is een gewone
semafoor die door de kernel zelf gepost wordt met een instelbare periode. De functie kan
9
nu zelf controleren of er werk is, maar zal niet continu draaien zodat threads met een
lagere prioriteit ook nog een kans krijgen.
2.3.2
Edge- vs. level-triggered interrupts
De filosofie van interruptroutines (Interrupt Service Routines of ISR’s) in de VDK is dat
deze zo kort mogelijk gehouden dienen te worden. In het ideale geval zal een ISR enkel
een semafoor posten en alle werk overlaten aan een gewone thread. Om deze filosofie ‘aan
te moedigen’ is het enkel mogelijk ISR’s in assembler te schrijven.
De bestaande TCP/IP stack hanteert dit principe niet: de interrupt handler voor de
ethernetinterface zal onder andere alle ontvangen pakketten uit de pakketbuffers op het
IC halen en ze stockeren in SDRAM geheugen. De code aangepast voor de VDK zal in de
ISR enkel een semafoor posten. Enige tijd later zal een thread specifiek voor afhandeling
van ethernetinterrupts het ISQ-register van de ethernetcontroller uitlezen en de nodige
acties ondernemen. Hierbij stoten we echter op het volgende probleem: een interrupt wordt
meestal aanzien als level-triggered. Dit wil zeggen dat, zolang de interruptpin actief is, de
interruptconditie aanwezig is en de ISR moet lopen. Wanneer, om een of andere reden,
de interruptpin nog steeds actief is na afloop van de ISR wordt deze situatie aanzien als
een nieuwe interrupt en zal de ISR opnieuw opgestart worden. In de ethernetdriver van
de oude versie is dit het gewenste gedrag: alle interruptcondities worden behandeld door
meerdere malen het ISQ-register uit te lezen. Wanneer alle interrupts verwerkt zijn geeft
dit register 0 terug en wordt de interruptpin van de ethernetcontroller gedesactiveerd.
De nieuwe ethernetdriver werkt echter op een andere manier: bij uitvoering van de
betreffende ISR wordt het ISQ-register helemaal niet uitgelezen. Het is dus normaal dat
de interruptpin nog steeds actief is bij het verlaten van de ISR. Opdat de DSP nu niet
opnieuw de ISR zou oproepen kan de interrupt ingesteld worden als edge-triggered. Het
is hierbij niet het niveau van de interruptpin dat belangrijk is, maar een transitie hierop.
Een inactief naar actief transitie triggert nu een interrupt en uitvoering van de ISR, het
niveau blijft actief tot wanneer de thread zijn werk gedaan heeft.
Tijdens het debuggen bleek echter dat transities op de interruptpin niet herkend worden
wanneer de processor geblokkeerd staat op een breekpunt, interrupts die op dat moment
binnenkomen leiden dus niet tot het uitvoeren van de ISR. Daarom werd de semafoor die
de ethernetthread opstart periodisch gemaakt, zodat bij het hernemen van de uitvoering
na een breekpunt steeds a.d.h.v. het ISQ-register gekeken wordt of de ethernetcontroller
een interrupt afgegeven heeft.
2.3.3
Big of little endian
Wanneer een woord moet worden opgeslagen in een medium met kleinere woordlengte
moet een volgorde uitgemaakt worden waarin men de verschillende deelwoorden wil op-
10
slaan. De big endian manier stockeert het meest significante deelwoord op het laagste
adres, de little endian manier doet net het omgekeerde. Dit probleem komen we tegen bij
het versturen van 32 bit IP-adressen over een byte-gestructureerd netwerk. Men gebruikt
hiervoor ‘network byte order’, bij Internet is dit big endian. Ook wanneer diezelfde bytegestructureerde netwerkdata in een 32 bit processor opgeslagen wordt zal dit in ofwel big
ofwel little endian gebeuren. De SHARC kan zelf geen woorden van 1 byte hanteren en
is uit zichzelf dus niet big of little endian, maar wanneer we bytes vanuit het ethernet
inlezen en samenvoegen tot 32 bit woorden moeten we wel zelf een formaat kiezen. Het
meest voor de hand liggende is het gebruik van big endian. Dit heeft het voordeel dat IP
adressen niet steeds omgekeerd moeten worden, zoals op bv. een x86 architectuur wel het
geval is.
De ethernetcontroller (zie paragraaf 2.1.2) veronderstelt echter een ISA bus en dus
een little endian processor. Bovendien werkt deze met slechts 16 bit woorden zodat we op
dit niveau opnieuw een opsplitsing moeten maken. Om een 32 bit woord in big endian te
versturen moeten we daarom eerst de hoogste 16 bits aan de ethernetcontroller aanbieden
met de eerste en de laatste 8 bits omgewisseld. Daarna doen we hetzelde met de laagste 16
bits uit het woord. Ontvangen van data gebeurt op de inverse wijze (zie ook figuur 2.3).
CS8900A
DSP
A
B
C
D
31
B
A
D
C
0
15
0
Figuur 2.3: Conversies nodig bij het versturen van een 32 bit woord over ethernet.
Dit is een eigenschap van de ethernetcontroller en hoeft niet gekend te zijn door de
rest van de code (de IP laag en hoger in de stack). De plaats om deze omwisselingen te
doen is dus logischerwijze in de ethernetdriver: vlak na het uitlezen van de pakketbuffers
bij ontvangst en bij het vullen van de pakketbuffers voor verzenden. Pakketten die in het
SDRAM geheugen horende bij de DSP zitten, staan dus steeds in big endian formaat. In
de bestaande code was deze opsplitsing om historische redenen niet gemaakt. Dit blijkt
echter het vlot onderhouden van de code in de weg te staan en is daarom aangepast in de
nieuwe versie.
2.3.4
Lijngebufferde sockets
De opzet van een server in de bestaande TCP/IP stack was vrij minimaal: deze bestond
uit slechts één functie server recv(). Deze werd door de stack aangeroepen telkens een
11
pakket ontvangen werd op de betreffende (TCP of UDP) poort met als argument de inhoud
van het pakket en een pointer naar de socket. Enige toestand die behoorde bij de socket
kon door de server bijgehouden worden; dit werd echter meestal niet gedaan. Dit was ook
niet nodig. De geı̈mplementeerde protocollen waren zo eenvoudig dat voor het formuleren
van een antwoord op een bepaald pakket enkel het huidige pakket nodig was en geen van
de voorgaande. Indien men iets ingewikkelder protocollen wil maken is het echter nodig
om een hoeveelheid informatie bij te houden over vorige pakketten. Dit is mogelijk door
aan de structuur die de socket voorstelt een userdata element toe te voegen. De server kan
dit element invullen met het adres van een structuur die alle benodigde data over deze
connectie bevat.
Bovendien werd erop gerekend dat één aanvraag (bv. een HTTP ‘GET’) ook in één
pakket past. De aanvraag is meestal wel kleiner dan de maximale pakketgrootte, maar
kan om andere redenen opgesplitst zijn in meerdere pakketten. Wanneer men bv. een
HTTP client wil emuleren d.m.v. een gewoon telnet programma wordt elk karakter dat
men intypt in een apart pakket verstuurd. De bestaande HTTP server zou het eerste
pakket, dat enkel de letter ‘G’ bevat, ontvangen, concluderen dat dit een ongeldig verzoek
is en een foutboodschap terugsturen. De correcte implementatie zou echter wachten op
een specifieke karaktersequentie, een dubbel regeleinde in het geval van HTTP, alvorens
te concluderen dat een volledig verzoek ontvangen werd. Een TCP verbinding verzorgt
immers communicatie in de vorm van een ononderbroken ‘stroom’. De pakketgrenzen
zijn gecreëerd door het protocol en mogen niet aanzien worden als informatie. Dit is in
tegenstelling tot het UDP procotol, waarbij één send() resulteert in één recv() en de
pakketgrenzen wel degelijk van belang zijn.
Omdat vele op TCP steunende protocollen gebaseerd zijn op communicatie per lijn
werd een extra (optionele) laag voorzien tussen de bestaande TCP implementatie en de
server die een buffering per lijn voor zich neemt. Het userdata element wordt hiertoe
gebruikt om per socket een buffer bij te houden die reeds ontvangen data bijhoudt, de
server recv() functie wordt slechts aangeroepen wanneer een volledige lijn beschikbaar
is.
2.4
2.4.1
Uitbreidingen op basis van de TCP/IP stack
Het boot-proces
De DSP heeft zelf geen niet-vluchtig geheugen aan boord. Na het opstarten zal de programmatuur dus vanuit een externe component in het interne geheugen geplaatst moeten
worden. Het is mogelijk dit interne geheugen te beschrijven over de JTAG bus. Deze vormt
tijdens het debuggen de verbinding met een PC. Dit gebeurt via de In Circuit Emulator
of ICE. Dit kan echter niet in een productie-omgeving, er is dus een vorm van boot-proces
12
nodig.
In de SHARC zijn hiervoor 3 mogelijkheden voorzien. Selectie van één ervan gebeurt
met een aantal inputpinnen die ‘hardwired’ of eventueel met jumpers op een hoog dan
wel laag logisch niveau gehouden worden. In de eerste mode wordt van een host-processor
verwacht dat deze de interne SRAM van de DSP zal beschrijven. In veel ontwerpen is de
DSP immers opgevat als een ‘coprocessor’ naast een gewone CISC of RISC processor. De
DSP is hiervoor voorzien van een Host Interface bus waarover de hostprocessor het interne
geheugen en een aantal van de registers van de DSP kan lezen en schrijven. In het voor de
VNA gebruikte ontwerp is de DSP de enige processor in het systeem, deze mogelijkheid
kunnen we dus niet gebruiken. Een tweede mogelijkheid is om in de plaats van één van de
SDRAM banken een ROM op te nemen. Dit vereist echter een ROM van 32 bit breed. Een
standaard ROM is bovendien te traag om aan een aanvaardbare snelheid code te kunnen
uitvoeren.
De derde mogelijkheid, en dit is tevens degene die bij de VNA gebruikt wordt, maakt
eveneens gebruik van een externe ROM. De SHARC heeft echter een speciale boot-mode,
waarin, vlak na het aanleggen van de voedingsspanning, de DSP autonoom 256 woorden
van 48 bit vanuit de ROM naar het interne geheugen kopieert. Deze ROM wordt aangesproken over een 8 bit verbinding met een groot aantal wachtcycli. Het is dus mogelijk
een goedkope, standaard ROM te gebruiken. Normaal is dit externe ROM geheugen niet
toegankelijk. Door echter een vlag in de processor te veranderen (BSO of Boot Select
Override) kunnen gegevens uit de ROM via een DMA kanaal naar het interne geheugen
gekopieerd worden. Het is dus mogelijk om in deze 256 woorden een boot-loader te plaatsen die de rest van het programma kopieert. Ook kan men op deze manier gedurende de
loop van het programma extra gegevens uit de ROM halen die niet continu nodig zijn.
2.4.2
Firmware-update
Het wordt echter interessanter wanneer we in plaats van een ROM een programmeerbare
component opnemen, bijvoorbeeld een Flash geheugen. Deze kunnen in het systeem geprogrammeerd worden, tegenwoordig zelfs zonder een speciale programmeerspanning. Het
is nu mogelijk om over de JTAG interface een programma in het interne of externe RAM
geheugen te plaatsen dat de DSP toelaat om het Flash geheugen te beschrijven. Op deze
manier kunnen we de firmware van de DSP aanpassen zonder dat de geheugenchip in een
speciaal programmeertoestel geplaatst hoeft te worden.
We hebben echter nog steeds de ICE nodig. Wanneer er een nieuwe firmwareversie
uitkomt, zal elke gebruiker die de update wil uitvoeren zijn toestel dus moeten terugsturen naar een onderhoudscentrum of een specialist ter plaatse laten komen. Het zou voor
de gebruiker veel kosten uitsparen mocht de productie-firmware zichzelf kunnen herprogrammeren. Hiervoor werd een module geschreven die toelaat nieuwe firmware over een
13
TCP-connectie binnen te halen en ze, na de nodige controles, in firmware wegschrijft. Deze
controles zijn echt wel belangrijk: indien de verkeerde firmware wordt weggeschreven zal
het toestel niet meer opstarten (of nog erger: in een gevaarlijke toestand terechtkomen!)
en is de tussenkomst van een specialist alsnog nodig.
De update-module luistert op TCP poort 87 naar inkomende connecties. Een eerste
beveiliging bestaat erin dat deze module niet steeds actief is. Dit gebeurt pas wanneer
over een controleconnectie de opdracht hiertoe gegeven wordt. Deze controleconnectie was
reeds geı̈mplementeerd en bestaat uit een soort van telnet verbinding waarmee enkele systeemparameters, zoals het IP adres en het geheugengebruik, bekeken en ingesteld kunnen
worden. Het is meestal niet wenselijk dat iedereen zomaar de firmware kan aanpassen
zodat de opdracht om de update-module te starten best met een wachtwoord beveiligd
wordt. Merk op dat indien dit toestel aan het publieke Internet hangt, om bijvoorbeeld
metingen vanop grote afstand te doen, potentieel iedereen met de VNA verbinding kan
maken! Het gebruik van een goede firewall die bijvoorbeeld enkel verbindingen toelaat die
vertrekken vanuit het eigen labo, kan dit probleem echter voor een groot deel oplossen.
Het is zelfs mogelijk om een firewall in de vorm van een pakketfilter te implementeren in
de IP laag van de VNA.
Van zodra de update-module gestart is kan de gebruiker een verbinding opzetten met
poort 87. De module verwacht eerst een aantal configuratieparameters. Het eerste woord
vormt een magic number, dit is een specifiek 32 bit getal dat afgesproken wordt met de
client. Op deze manier hebben we een controle dat de client en de server hetzelfde protocol
gebruiken. Vervolgens komt het adres waar de data geschreven dient te worden. Het Flash
geheugen bevat namelijk niet alleen de firmware, maar ook het programmeerbestand voor
de FPGA, de netwerkinstellingen (IP adres, netmask, gateway, . . . ) en een bestandssysteem voor de webserver (meer hierover in paragraaf 2.4.3). Deze blokken staan in aparte
‘pagina’s’ in het Flash geheugen die afzonderlijk gewist en geherprogrammeerd kunnen
worden. Tot slot volgen een checksum over de data die geschreven dienen te worden, en
één over de zopas verstuurde header. Indien deze laatste niet klopt, wordt meteen een
signaal aan de client gegeven zodat deze de tot wel enkele honderden kilobytes grote programmeerfile niet nodeloos hoeft door te sturen. Indien alles tot hiertoe in orde is kan
de client de eigenlijke programmeerfile verzenden. Hiervan wordt een checksum berekend
en indien deze overeenkomt met deze in de header, wordt de data in het Flash geheugen
geprogrammeerd.
Indien men steeds werkt met de programmeerbestanden die de fabrikant verstrekt, is
het nu onwaarschijnlijk dat de foutieve data in het Flash geheugen terechtkomen. Wat wel
nog kan misgaan is dat de voedingsspanning onderbroken wordt tijdens het programmeren, met als gevolg dat het toestel niet meer opstart. Het is dan onmogelijk opnieuw een
herprogrammatie te proberen zonder gespecialiseerde tools (een ICE en de juiste ontwikkelomgeving). Een mogelijke oplossing hiervoor is om een pagina in het Flash geheugen
14
te voorzien als ‘reddingsboei’. Dit stuk programmatuur is het eerste wat de DSP uitvoert.
Het controleert of de rest van de firmware geldig is. Indien dit het geval is wordt deze uitgevoerd; indien niet komt het toestel in een staat waarin een nieuw programmeerbestand
aangeboden kan worden. Deze reddingsboei mag uiteraard nooit overschreven worden. Het
is dus ook niet mogelijk hiervan een nieuwe versie te verdelen zoals de bedoeling is met de
rest van de firmware. Ze wordt liefst ook zo eenvoudig mogelijk gehouden. Overdracht van
de programmeerfile over TCP/IP lijkt hiervoor een slechte keuze, temeer omdat dit een
grote hoeveelheid code vereist die bijna nooit uitgevoerd wordt. Een RS-232 seriële poort
is waarschijnlijk een betere keuze al vereist dit uiteraard wel extra hardware.
2.4.3
Bestandssysteem in Flash
Om een toestel via het Internet te besturen is een hoeveelheid software nodig die op de PC
van de gebruiker draait. Meestal wordt deze software op voorhand op elk van de gebruikte
PC’s geı̈nstalleerd, en is een afzonderlijke versie nodig voor elk besturingssysteem dat men
wil ondersteunen. Dit brengt uiteraard veel werk met zich mee. Het laatste probleem kan
men oplossen door de software uit te voeren als Java applet. Dit draait immers onder
een groot aantal besturingssystemen op verschillende hardwareplatformen. De distributie
van deze software kan men aanpakken door in het toestel zelf een webserver te integreren. De gebruiker hoeft nu enkel d.m.v. een gewone Internet browser naar de VNA te
‘surfen’, de nodige applets kunnen dan automatisch gedownload worden. Bovendien is een
geı̈ntegreerde webserver interessant om de algemene status van het toestel te monitoren.
Een voorbeeld hiervan is de TrendView reeks dataloggers van Honeywell; een dergelijke
recorder is toegankelijk op http://recorder1.trendview.com/.
In de bestaande versie van de TCP/IP stack was reeds een elementaire webserver
geı̈ntegreerd. Bestanden die deze webserver kon hosten moesten echter telkens na het opstarten ingevoerd worden. Het is uiteraard wenselijk dat deze bestanden deel uitmaken
van de firmware en in Flash opgeslagen kunnen worden. We willen dus een aantal bestanden opslaan in een aaneensluitend stuk Flash geheugen. Om deze bestanden te kunnen
terugvinden hebben we een bestandssysteem nodig. De bestanden maken deel uit van de
firmware. Het is niet de bedoeling dat de gebruiker deze aanpast of er nieuwe bijmaakt.
Dit vereenvoudigt het bestandssysteem gevoelig. We kunnen dan ook alle bestanden achter elkaar in het geheugen plaatsen en hebben enkel nog een structuur nodig die van elk
bestand de naam, grootte en offset weergeeft. Deze structuur hebben we nodig voor elke
bestandstoegang. Het is dus voordelig deze naar SDRAM te kopieren daar een toegang tot
het Flash geheugen vrij traag verloopt. Dit gebeurt wanneer de eerste keer een bestand
opgevraagd wordt.
15
2.5
Besluit
Op dit platform werd niet de volledige functionaliteit nodig voor een werkende VNA
geı̈mplementeerd. Toch liet de uitwerking van enkele subsystemen toe een goed beeld te
krijgen van de voor- en nadelen van dit platform. Een overzicht hiervan en een vergelijking
met het alternatieve platform dat in de volgende hoofdstukken besproken zal worden, is
opgenomen in hoofdstuk 10.
16
Hoofdstuk 3
Implementatie met Linux en USB
3.1
Inleiding
Het besturingssysteem Linux heeft de laatste jaren sterk aan populariteit gewonnen. Linux
is sterk netwerk-georiënteerd en is dus ideaal om bv. webservers op te draaien, en het is
voldoende flexibel om eigen functionaliteit te kunnen inbouwen. Het besturen van hardware
is dan ook zeer goed mogelijk. Er bestaan zelfs distributies die toelaten dit in hard realtime te doen. Linux begint tegenwoordig dan ook snel ingang te vinden in embedded
systemen.
Vanuit dit perspectief is een keuze voor Linux voor het besturen van de VNA zeer
interessant: een TCP/IP stack en webserver zijn volledig gratis, de VNA specifieke software
die geschreven moet worden, kan steunen op een volwaardig OS. Een minpunt aan deze
oplossing is dat Linux vrij zware systeemvereisten heeft en bijvoorbeeld niet draait op de
SHARC. Een PC voldoet uiteraard wel, dit kan bijvoorbeeld de PC van de gebruiker zijn
waar we de VNA rechtstreeks mee verbinden. Het voordeel van meting op grote afstand
valt hiermee echter weg. Een andere oplossing is om een lichtgewicht PC te integreren in
de VNA. Deze laatste is de weg die we verder gevolgd hebben. Als PC viel de keuze op
een pas uitgebracht moederbord van VIA uit de EPIA-M reeks. Deze zijn van het miniITX formaat (17 bij 17 cm) maar hebben toch dezelfde functionaliteit dan een gewoon
ATX moederbord (grootteorde 30 bij 25 cm). Bovendien ligt het stroomverbruik van deze
bordjes een stuk lager, vooral door de gebruikte processor. Het hier gebruikte bord heeft
een 900 MHz VIA C3 processor aan boord, met een verbruik van slechts 1 Watt (ter
vergelijking: een 2.40 GHz Pentium 4 verbruikt tot 58 Watt!).
Tenslotte moeten we beslissen op welke manier de VNA verbonden zal worden met de
PC of het VIA moederbord. Een standaard aanwezige perifere bus is uiteraard de beste
oplossing. Dit vermijdt het gebruik van insteekkaarten. De keuze viel uiteindelijk op USB
2.0; deze bus biedt namelijk voldoende bandbreedte en is reeds goed verspreid. Bovendien
is een ruim assortiment van client-side controllers beschikbaar om de verbinding te vormen
17
Figuur 3.1: Het VIA EPIA-M moederbord.
tussen USB en de te besturen elementen van de VNA: de FPGA en een Z8 processor die
de configuratie van de S-parameter testset beheert. Hiervoor werd de FX2 van Cypress
gekozen. Dit is een volledige USB-tranceiver met een geı̈ntegreerde microcontroller uit de
8051 familie. Interessant is dat de verbinding tussen USB en de FPGA gevormd wordt door
een aantal FIFO’s op de FX2, de (niet zo snelle) 8051 controller zit niet in dit datapad,
maar wordt enkel gebruikt voor het initialiseren ervan. Een verdere beschrijving van dit
IC volgt in paragraaf 4.2.
3.2
Structuur
De programmatuur die op de beide processoren draait, implementeert een aantal afzonderlijke functies. Het is dan ook belangrijk in een goede structuur te voorzien. We willen
liefst de technicaliteiten van één laag verbergen en een eenvoudige interface aanbieden aan
de laag erboven. Een overzicht van de verschillende componenten en hun relaties is te zien
in figuur 3.2. Een korte beschrijving van de functionaliteit wordt nu gegeven, een volledige
bespreking wordt gegeven in de hoofdstukken 4 tot en met 7.
18
De onderste laag bestaat uit de firmware voor de FX2. Deze zorgt ervoor dat de
verbinding tussen de FPGA en de USB geı̈nitialiseerd wordt. Ook worden een aantal
opdrachten, die eveneens over USB komen, uitgevoerd. Deze zijn bijvoorbeeld het versturen
van data over de seriële verbinding naar de Z8 of het programmeren van de FPGA.
Alle hogere lagen bevinden zich op het VIA moederbord en draaien onder de Linux
kernel. Eerst vinden we een device-driver. Dit is de enige component die (als module)
binnen de kernel draait. De andere componenten zijn uitgevoerd als proces en bevinden zich
in user space. Een onderdeel van de kernel is de Linux usb core. Deze bestaat uit een aantal
drivers die met de hardware van de USB interface werken. Een eigen driver hoeft dus niet
rechtstreeks met de hardware te werken. Dit is immers niet zomaar mogelijk: aan een USB
bus kunnen verschillende devices hangen, elk met hun eigen driver. Deze communiceren
via een API met een onderdeel van de Linux kernel dat de eigenlijke communicatie met
de devices verzorgt. De belangrijkste functie van deze API is usb submit urb(). Deze
registreert een aanvraag tot verzenden of ontvangen van een USB pakket.
Een bovenliggende laag kan met de device driver communiceren d.m.v. drie functies:
read(), write() en ioctl(). De eerste twee werken rechtstreeks op de FIFO’s van de
FX2 en laten communicatie met de FPGA toe. Via ioctl() kunnen een aantal instellingen
van de driver aangepast worden, en kunnen ook opdrachten gegeven worden aan de 8051
binnenin de FX2. Het versturen van data naar de Z8 gebeurt bijvoorbeeld op deze manier.
Voor een bovenliggende laag zijn lezen en schrijven eenvoudig genoeg, maar de ioctl()
call vergt teveel kennis van het onderliggende systeem. Men moet namelijk de juiste parameters meegeven zodat de driver weet dat het om een opdracht voor de 8051 gaat, en
een datapakket opstellen waarme de 8051 weet dat hij bv. een byte over de seriële poort
moet sturen. Al deze functionaliteit is samengepakt in vna com send(); samen met een
aantal andere vna *() routines vormt dit de LibVNA component. De bedoeling is dat de
combinatie van firmware, driver en LibVNA transparant is, en dat de bovenliggende laag
d.m.v. eenvoudige functie-oproepen toegang heeft tot de verschillende interfaces (FIFO,
serieel, I2 C) van de FX2.
Tenslotte volgt de laag die de eigenlijke besturing van de VNA op zich neemt, nl.
WebVNA. Deze laag leest de meetwaarden van de FPGA uit en stuurt ze door naar het
clientprogramma en ontvangt instellingen zoals de gewenste frequentiepunten en geeft ze
door aan de hardware.
19
Client
TCP/IP
WebVNA
vna *()
read()
write()
LibVNA
ioctl()
USB FX2 driver
VIA + Linux
USB
FIFO
FX2
Parallel
8051
Seriëel
FPGA
Z8
Figuur 3.2: Structuur van de verschillende componenten.
20
Hoofdstuk 4
Het USB Client-device
4.1
De USB Interface
De centrale component in dit deel van deze thesis is uiteraard de USB interface. In deze
paragraaf wordt een kort overzicht gegeven van deze bus. De volledige specificatie is te
vinden in [USB2.0].
De originele Universal Serial Bus is ontworpen in 1995 en was bedoeld om op een
snelle, eenvoudige manier een groot aantal randapparaten met één PC te verbinden. De
tot dan toe gebruikte parallelle en seriële poorten waren sterk beperkt in aantal en konden
elk slechts één randapparaat aan; meestal was het nodig dat de gebruiker nog een aantal
instellingen configureerde vooraleer communicatie mogelijk was. Met USB daarentegen
kunnen tot 127 randapparaten op één bus aangesloten worden en moet de gebruiker niet
weten op welke bus of aan welke snelheid een apparaat werkt. Het besturingssysteem zal
zelf het inpluggen van een apparaat detecteren en zonder tussenkomst van de gebruiker het
juiste stuurprogramma inladen. De eerste versie van USB ondersteunt twee snelheden, nl.
1.5 Mbit/s en 12 Mbit/s. Met USB 2.0 werd in 1999 hieraan een derde snelheid toegevoegd
van 480 Mbit/s.
De master in een USB systeem, dit is bijna altijd een PC, wordt de host genoemd.
Het is deze die alle communicatie initieert. De randapparaten of devices kunnen dus niet
rechtstreeks met elkaar communiceren en kunnen zelf ook geen transfer met de host starten.
De bus zelf heeft een boomstructuur. De host staat aan de top. Elk device bevindt zich
in een bladknoop. Het is mogelijk hubs op te nemen: deze zorgen ervoor dat op één poort
meerdere devices aangesloten kunnen worden. Omwille van de vertragingen geı̈ntroduceerd
door de hubs mogen deze tot maximum 5 niveaus diep gebruikt worden.
In de host bevindt zich een Host Controller. Dit is het eigenlijke interface-IC, aangevuld met een hoeveelheid software, daar niet alle functies in hardware uitgevoerd worden.
Voor USB 1.1 werden twee standaarden voor host controllers gedefinieerd, UHCI (Universal Host Controller Interface) en OHCI (Open HCI). Een UHCI controller bevat minder
21
1 THE UNIVERSAL SERIAL BUS
5
USB
Host Controller
Virtual Root Hub
Hub
Device
Device
Downstream
Upstream
Device
Hub
Device
Device
Figure 1: USB Topology
Figuur 4.1: Topologie van een USB systeem.
Even en
thekan
maximum
communication
differ for particular
USB dehardware
dus goedkoper
uitgevoerd speed
worden.can
Daartegenover
staat uiteraard
dat
vices. The USB specification decides between low speed and full speed devices. Low speed devices (such as mice, keyboards, joysticks etc.) communiovergenomen, wat voor een bijkomende belasting van de CPU zorgt. Voor USB 2.0 is er
cate at 1.5MBit/s and have only limited capabilities. Full speed devices (such
slechts
éénand
standaard,
nl. EHCI (Enhanced
HCI).
as audio
video systems)
can use up
to 90% of the 12Mbit/s which is about
Wanneer
de
host
controller
merkt
dat
een
nieuw
device in het systeem gebracht is, zal
10Mbit/s including the protocol overhead.
de functionaliteit die niet in de hardware opgenomen is door de software moet worden
deze eerst de descriptors van dit device opvragen. Dit proces wordt enumeratie genoemd.
In
deze descriptors
staat alle informatie die voor het USB protocol van belang is. Eén
1.2.1
Hubs
van de parameters is de hoeveelheid opgenomen stroom. De bus kan immers tot 500 mA
Physically
exist
a number
of USB
at theIDrear
panel
of a computer.
voeding
aan there
elk device
leveren.
Ook vinden
weports
een Vendor
(VID)
en Product
ID (PID).
These ports
canhost
be om
used
to type
attach
normal
devices
or de
a hub.
A driver
hub isingeladen
a USB
Hiermee
weet de
welk
device
het gaat
en kan
gepaste
device which extends the number of ports (i.e. 2-8) to connect other USB
devices. The maximum number of attachable devices is reduced by the number
Een gewone parallelle of seriële poort heeft slechts één kanaal. Het is dus aan het
of hubs
on the bus. Hubs are self- and/or bus powered full speed devices.
device
zelf
om onderscheid
te maken
tussen
en controleinformatie.
werd
Normally
the physical
ports of
the gegevens
host controller
are handled Bij
by USB
a virtual
dit
opgelost
te voorzien
in endpoints.
Dit is een
manier
om verschillende
rootprobleem
hub. This
hub isdoor
simulated
by the
host controller’s
device
driver
and helps
gegevensstromen
multiplexeren
over één
fysische
verbinding.
kansame
controleinformatie
to unify the bustetopology.
So every
port
can be
handledMen
in the
way by the
USBéén
subsystem’s
huben
driver
figuregegevensstromen
1).
over
endpoint sturen
één of(see
meerdere
elk over hun eigen endpoint.
worden.
Een endpoint is dus te vergelijken met een TCP poort. De toekenning ervan is echter
statisch.
De beschrijving
hiervan gebeurt in de endpoint descriptors. Deze bevatten van
1.2.2 Data
Flow Types
elk gebruikt endpoint de richting (IN is van device naar host, OUT van host naar device),
The communication on the USB is done in two directions and uses 3 different
transfer types. Data directed from the host to a device is called downstream or
22
het type en eventueel een aantal extra parameters. Een endpoint wordt afgekort als EP?IN
of EP?OUT, met ? een nummer van 0 tot 15. Er zijn dus 32 endpoints beschikbaar. Een
endpoint wordt immers als unidirectioneel beschouwd, zodat EP0IN en EP0OUT twee
verschillende endpoints zijn.
EP0IN en EP0OUT zijn steeds kanalen van het type control. Deze worden gebruikt
om de USB interface van het device te configureren, zoals het overhalen van endpoint
descriptors of het kiezen van een alternatieve configuratie met andere endpoints. De overige
30 endpoints staan ten dienste van de gebruiker. Men kan voor elk van deze endpoints één
van drie types kiezen: bulk, interrupt (int) of isochronous (iso).
Bulk transfers zijn gegarandeerd verliesloos door het gebruik van foutdetectie. Indien
nodig wordt de transfer herhaald. Dit type wordt meestal gebruikt voor het versturen van
een grote hoeveelheid data waar geen specifieke timingvereisten aan verbonden zijn (bv.
communicatie met een printer).
Zoals gezegd zal de host alle communicatie initiëren. Wanneer het device toch uit
eigen initiatief gegevens wil verzenden, kan dit met een endpoint van het type interrupt.
De host zal met een door de gebruiker instelbare periode het device pollen. Dit garandeert
het overbrengen van een (kleine) hoeveelheid data met een gelimiteerde vertraging.
Wanneer een grotere hoeveelheid data met specifieke timing-vereisten verstuurd moet
worden (bv. audiodata naar een luidspreker) kan men een endpoint van het type iso gebruiken. Er wordt vooraf opgegeven wat het vereiste debiet is. De host controller zal nagaan
of het totaal van alle iso en int transfers niet het totale debiet van de bus overschrijdt.
Indien dit wel het geval is, wordt de transfer geweigerd; de gebruiker kan dan eventueel
een lager debiet kiezen door bv. de geluidskwaliteit te verlagen. Omwille van het real-time
karakter van iso transfers wordt een mislukte communicatie niet opnieuw geprobeerd.
Communicatie over dit type endpoint is dus niet gegarandeerd verliesloos.
4.2
Cypress FX2
De FX2 van Cypress is een complete USB transceiver met een geı̈ntegreerde microcontroller, gebaseerd op de populaire 8051. Het IC bevat alle benodigde logica om een USB device
te vormen en kan dan ook rechtstreeks met een USB kabel verbonden worden. Bovendien
is 8 KiB RAM aanwezig die via de USB interface gelezen en geschreven kan worden. Er is
dus geen nood aan een ROM programmageheugen, daar de host alle benodigde firmware
kan inladen. Een aantal hogesnelheids FIFO’s laten toe om gegevens in woorden van 8 of
16 bit door te sturen tussen een externe component en de USB interface aan een burst
snelheid tot 96 MB/s. Dit gebeurt zonder tussenkomst van de microcontroller; deze is
hiervoor immers veel te traag. De 4 FIFO’s werken steeds op de endpoints EP2, EP4,
EP6 en EP8. Door de 8051 de juiste registers te laten beschrijven kunnen deze FIFO’s
geconfigureerd worden voor invoer of uitvoer en kan een pakketlengte ingesteld worden. Af-
23
effective solution that provides superior time-to-market advantages. The ingenious architecture of FX2 results in data transfer
rates of 56 Mbytes per second, the maximum allowable USB 2.0 bandwidth, while still using a low-cost 8051 microcontroller in
a package as small as a 56 SSOP. Because it incorporates the USB 2.0 transceiver, the FX2 is more economical, providing a
smaller footprint solution than USB 2.0 SIE or external transceiver implementations. With EZ-USB FX2, the Cypress Smart SIE
handles most of the USB 1.1 and 2.0 protocol in hardware, freeing the embedded microcontroller for application-specific functions
and decreasing development time to ensure USB compatibility. The General Programmable Interface (GPIF) and Master/Slave
Endpoint FIFO (8- or 16-bit data bus) provides an easy and glueless interface to popular interfaces such as ATA, UTOPIA, EPP,
PCMCIA, and most DSP/processors.
Four packages are defined for the family: 56 SSOP, 56 QFN, 100 TQFP, and 128 TQFP.
High-performance micro
using standard tools
with lower-power options
VCC
x20
PLL
/0.5
/1.0
/2.0
Data (8)
Address (16)
FX2
8051 Core
12/24/48 MHz,
four clocks/cycle
1.5k
connected for
full speed
D+
D–
USB
2.0
XCVR
Integrated
full- and high-speed
XCVR
8.5 kB
RAM
CY
Smart
USB
1.1/2.0
Engine
I 2C
Compatible
Master
Address (16) / Data Bus (8)
24 MHz
Ext. XTAL
ADDR (9)
GPIF
RDY (6)
CTL (6)
4 kB
FIFO
Enhanced USB core
Simplifies 8051 core
“Soft Configuration”
Easy firmware changes
Abundant I/O
including two USARTS
Additional I/Os (24)
8/16
General
programmable I/F
to ASIC/DSP or bus
standards such as
ATAPI, EPP, etc.
Up to 96 MBytes/s
burst rate
FIFO and endpoint memory
(master or slave operation)
Figure 1-1. Block Diagram
• Single-chip integrated USB 2.0 Transceiver, SIE, and Enhanced 8051 Microprocessor
Figuur 4.2: Blokschema van de Cypress FX2.
• Software: 8051 runs from internal RAM, which is:
— Downloaded via USB, or
— Loaded from
EEPROM
hankelijk
hiervan
en van het aantal endpoints dat daadwerkelijk gebruikt wordt, kan men
— External memory device (128-pin configuration only)
het beschikbare buffergeheugen van 4 KiB verdelen over de pakketbuffers die de FIFO’s
• Four programmable BULK/INTERRUPT/ISOCHRONOUS endpoints
— Buffering options:Men
double,
triple
quad uit dubbele, drievoudige of viervoudige buffering.
implementeren.
heeft
deand
keuze
• 8- or 16-bit external data interface
Voor een pakket dat in de buffers opgeslagen is, maakt men onderscheid naargelang
• GPIF
dit
zich direct
in het
‘USB domein’
of in interfaces;
het ‘FIFO
domein’
— Allows
connection
to most parallel
8- and
16-bit bevindt (zie figuur 4.3). Wanneer
— Programmable
waveform
descriptors
and configuration
registers towordt,
define waveforms
een
pakket door
de USB
host naar
de FX2 geschreven
zal dit pakket in het USB
— Supports multiple Ready (RDY) inputs and Control (CTL) outputs
domein terechtkomen. De inhoud van het pakket is op dit moment nog niet toegankelijk
• Integrated, industry standard 8051 with enhanced features:
— Upde
to 48-MHz
clock
via
FIFO’s;
de rate
8051 kan de inhoud ervan echter wel lezen. Overdracht naar het FIFO
— Four clocks per instruction cycle
domein
kan op twee manieren: de 8051 kan voor elk pakket afzonderlijk deze overdracht
— Two USARTS
bevelen via het OUTPKEND register; men kan deze overdracht ook automatisch laten
plaatsvinden door het AUTOOUT bit op 1 in te stellen.
Document #: 38-08012 Rev. *C
Page 6 of 52
Gegevens die door de externe component geschreven worden, volgen de omgekeerde
weg. Alle geschreven woorden worden geaccumuleerd in één pakket, totdat één van volgende condities zich voordoet:
• het AUTOIN bit is op 1 ingesteld en het pakket bereikt een grootte gelijk aan deze
in het AUTOINLEN register;
• de externe component maakt de PKTEND pin actief; of
• de 8051 schrijft naar het INPKTEND register.
Vanaf dan zal het pakket zich in het USB domein bevinden. Wanneer de USB host nu een
24
AUTOOUT=1
OUTPKTEND
OUT
transfer
-
PC
usb
IN
transfer
FIFO
read
-
USB domein
-
FIFO domein
AUTOIN=1
INPKTEND
fifo FPGA
FIFO
write
Figuur 4.3: Onderverdeling tussen USB en FIFO domein.
leesbewerking uitvoert zal het pakket verstuurd worden.
Het USB en het FIFO domein bevinden zich fysisch in hetzelfde geheugen; bij een
overdracht wordt dus niets gekopieerd. De beschikbare plaats – 2, 3 of 4 pakketten groot,
onafhandelijk van de grootte van een pakket – wordt daarom gedeeld tussen de beide
domeinen.
Verder worden ook het controle-endpoint EP0 en de extra endpoints EP1IN en EP1OUT
in hardware geı̈mplementeerd. Deze kunnen gebruikt worden om rechtstreeks met de 8051
te communiceren.
Ook voorzien is een General Purpose Interface (GPIF). Dit is een programmeerbare
state machine waarmee men m.b.v. de FIFO’s en een aantal extra status- en controlelijnen
een willekeurige interface kan implementeren. Zo kan men het gebruik van een speciale
buscontroller vermijden tussen de FIFO’s en bv. een IDE device. De GPIF wordt voor de
VNA echter niet gebruikt daar we in de FPGA voldoende intelligentie kunnen inbouwen.
Tenslotte zijn ook alle externe interfaces aanwezig die men op een moderne 8051 verwacht: 2 seriële poorten (met zowel een sychrone als een asynchrone mode), een I2 C controller en een aantal poorten voor parallelle I/O.
4.3
Firmware voor de FX2
Wanneer de FX2 op de USB aansluiting van een PC aangesloten wordt, zal de host controller detecteren dat een nieuw device aanwezig is. De descriptors van het device worden
overgehaald en de PC zal de VID/PID combinatie herkennen als een niet-geprogrammeerde
FX2. Vervolgens kan de PC via het controle-endpoint de benodigde firmware in het in-
25
Opdracht
COM1 SEND byte
Antwoord
status
I2C READ adres len
status len data . . .
I2C WRITE
adres len data . . .
PING
status 0
FPGA PROGRAM
0
FPGA PROGRAM END
status
SET EP6 LEN
lengte
0
Beschrijving
Verstuurt byte over seriële poort 1 naar
de Z8.
Leest len bytes van I2 C-adres adres en
retourneert deze in data.
Schrijft de bytes data naar I2 C-adres
adres.
Kan gebruikt worden voor testdoeleinden.
Start de programmeersequentie voor de
FPGA. Zie paragraaf 4.4 voor details.
Einde van de FPGA programmeersequentie. Zie paragraaf 4.4.
Stel het aantal bytes in die de FPGA
geschreven moet hebben vooraleer een
pakket naar het USB domein overgedragen wordt.
Tabel 4.1: Opdrachten voor de FX2.
terne RAM geheugen schrijven. Heel dit proces wordt in hardware ondersteund, de 8051
wordt gedurende dit procédé in reset gehouden. Tenslotte zal de PC aan de USB tranceiver
opdracht geven om de reset-pin van de 8051 inactief te maken.
Het eerste wat de 8051 doet, is de verbinding met USB verbreken en direct weer
herstellen. Dit is een proces dat door Cypress ReNumeration genoemd wordt. De PC zal
nu veronderstellen dat het eerste device niet meer aangesloten is, maar dat een nieuw device
aanwezig is. Opnieuw zullen de descriptors overgehaald worden. Deze zijn echter anders
dan de eerste keer, daar samen met de firmware een nieuwe set descriptors geı̈nstalleerd
wordt. De PC zal het nieuwe device daarom herkennen als een geprogrammeerde VNA
en de juiste driver laden. Dit alles kan in Linux automatisch gebeuren met Hotplug, of
manueel door zelf fxload aan te roepen.
Vervolgens zal de firmware de externe interfaces initialiseren. In het door ons gebouwde
systeem worden endpoints EP2OUT en EP6IN gebruikt voor communicatie met de FPGA,
de overige twee FIFO’s worden niet gebruikt. We werken met een pakketgrootte van 512
bytes (het maximum voor USB 2.0) en kunnen beide FIFO’s dus viervoudig bufferen.
Dit relaxeert de eisen op de performantie van de PC. Deze kan nu meer tijd laten tussen
het uitlezen van de FIFO’s. Verder wordt een seriële poort ingesteld voor communicatie
met de Z8 en worden een aantal parallelle pinnen geconfigureerd voor het doorgeven van
status-informatie aan de FPGA.
Wanneer de hardware geı̈nitialiseerd is, komt de 8051 in een lus waarin hij wacht
op opdrachten die door de PC over EP1OUT verstuurd worden. Zo’n opdracht bestaat
26
steeds uit één pakket. De eerste byte geeft het type opdracht aan. Dit is een constante
die gedefinieerd wordt in het bestand firmware/vna.h. De overige bytes kunnen gebruikt
worden om extra gegevens mee te sturen. Het resultaat wordt steeds teruggegeven d.m.v.
een pakket via EP1IN. De geı̈mplementeerde opdrachten zijn te vinden in tabel 4.1.
4.4
Programmeren van de FPGA
Na het aanleggen van de voedingsspanning moet eveneens de FPGA geprogrammeerd worden. De FPGA heeft hiertoe 3 inputpinnen CLK, DIN en P ROGRAM en 2 outputpinnen
IN IT en DON E. De programmeersequentie is als volgt: eerst wordt de P ROGRAM pin
hoog, laag en weer hoog gebracht. De FPGA zal hierop reageren door IN IT hoog en
DON E laag te brengen om aan te geven dat deze klaar is om een programmeerbestand
te ontvangen. Vervolgens worden 1.335.840 bits aangelegd op DIN , die met een stijgende
flank op CLK ingeklokt worden. Wanneer alle bits ontvangen zijn, zal de FPGA hiervan
een CRC berekenen om te controleren of er geen fouten opgetreden zijn. Indien alles in
orde, is zullen IN IT en DON E beide hoog gemaakt worden.
Het is de bedoeling dat de FX2 deze programmatie op zich neemt. Wanneer via
EP1OUT de opdracht FPGA PROGRAM wordt ontvangen zal de FX2 de programmeersequentie starten. De P ROGRAM pin wordt op het juiste niveau gebracht en de FIFO
behorende bij EP2OUT wordt uitgeschakeld. De PC kan nu de programmeerdata voor
de FPGA doorsturen over EP2. Deze zal de uitgangspinnen van de FIFO niet bereiken.
De data zal daarentegen gebruikt worden door de 8051; wanneer de data van een pakket
verstuurd is, wordt dit pakket gewist door het OUTPKTEND register te beschrijven. Na
ontvangst van de opdracht FPGA PROGRAM END worden de pinnen IN IT en DON E
uitgelezen en doorgegeven aan de PC, en wordt de FIFO van EP2 terug geactiveerd. Tenslotte wordt een pin van de FPGA hoog gebracht zodat deze weet dat de FX2 klaar is om
gegevens te ontvangen.
Het aansturen van P ROGRAM en het uitlezen van IN IT en DON E is eenvoudig:
deze worden elk verbonden met een van de pinnen van een parallelle interface en zijn voor
de code rechtstreeks toegankelijk. We kunnen met CLK en DIN hetzelfde doen en de
data versturen met volgend stuk code. De data bevindt zich op adres EP2FIFOBUF, de
lengte ervan staat in EP2BCH en EP2BCL:
27
for(i = 0; i < EP2BCH << 8 + EP2BCL; ++i)
{
b = EP2FIFOBUF[i];
for(j = 7; j >= 0; --j)
{
CLK = 0;
DIN = (b >> j) & 1;
CLK = 1;
}
}
Het probleem hierbij is echter dat de 8051 enkel shifts van 1 positie kan uitvoeren. De
regel DIN = (b >> j) & 1 zal daarom door de compiler vertaald worden in een lus die
j keer uitgevoerd wordt. De gegenereerde assemblercode voor deze instructie is te vinden
in figuur 4.4. Dit heeft tot gevolg dat de tijd van één bit toeneemt met de grootte van j
(zie figuur 4.5). Dit is voor de FPGA geen probleem, daar de tijd tussen de flanken van
CLK eveneens toeneemt, het kan echter sneller. Maar zelfs wanneer we de code aanpassen
zodat er per bit slechts één verschuiving nodig is (door het resultaat van de vorige iteratie
te gebruiken), halen we nog steeds maar een snelheid van enkele honderden kilobit per
seconde. Het programmeerbestand is 1,3 Mbit groot. Het programmeren zal dus meerdere
seconden in beslag nemen.
Een betere oplossing maakt gebruik van de seriële poort die op de 8051 aanwezig
is. Deze heeft een synchrone mode waarbij de klok naar buiten gebracht wordt op de
TXD0 pin; de data staat op RXD0OUT. Een minpunt is dat deze seriële poort de minst
significante bit eerst verstuurt, terwijl de programmeerbestanden opgesteld zijn om met de
meest significante bit eerst verstuurd te worden. De PC kan dit bestand echter eenvoudig
aanpassen.
MOV
MOV
INC
SJMP
A, b
R0, j
R0
?C0114
CLR
RRC
C
A
DJNZ
RRC
MOV
R0,?C0113
A
DIN,C
?C0113:
?C0114:
Figuur 4.4: Resultaat na compilatie van DIN = (b >> j) & 1.
28
CLK
3
2
1
0
DIN
3
2
1
0
0
5
10
15
20
25
Tijd (µs)
Figuur 4.5: Signaalverloop van CLK en DIN met software lus.
Vervolgens vragen we ons af op welke manier we de data uit de USB buffers naar de
seriële poort sturen. Meestal wordt dit gedaan in een interruptroutine, zodat de processor
iets anders kan doen terwijl een byte verstuurd wordt. De snelheid ligt hier echter zo
hoog (12 Mbit/s) dat de overhead hiervan veel te groot is. We moeten dus een lus maken
die niets anders doet dan het verplaatsen van bytes. Het blijkt dat het zelfs op deze
manier moeilijk is om de volle snelheid te halen daar de C-compiler geen echt efficiënte
code genereert. Bij 12 Mbit/s is de tijd om één bit te versturen namelijk even groot als
de tijd om één eenvoudige instructie uit te voeren (de meeste instructies duren echter
2 of 3 bittijden); we moeten dus een lus maken van maximaal 8 instructies lang. Dit is
enkel mogelijk door deze rechtstreeks in assembler te programmeren. Hierbij botsen we
echter op een nieuwe beperking van de 8051. Er is slechts één dataregister aanwezig in de
verzendeenheid. Bovendien duurt het twee bittijden vooraleer een geschreven woord ook
effectief verstuurd wordt. Er is dus een dode tijd van twee bits tussen elk 8 bit woord, wat
in een effectieve doorvoersnelheid van slechts 9,6 Mbit/s resulteert. Het signaalverloop van
CLK en DIN in dit geval is weergegeven in figuur 4.6. Rekening houdend met het feit dat
de PC de USB buffers niet altijd even snel kan vullen dan dat ze naar de FPGA gestuurd
worden, kan de programmeertijd toch worden teruggebracht tot minder dan 200 ms.
29
CLK
3
2
1
0
DIN
3
2
1
0
0
0.2
0.4
0.6
0.8
1
Tijd (µs)
1.2
1.4
1.6
Figuur 4.6: Signaalverloop van CLK en DIN met gebruik van USART0.
30
1.8
Hoofdstuk 5
USB in Linux
5.1
Het Linux USB subsysteem
In tegenstelling tot bijvoorbeeld RS232 is het USB protocol vrij uitgebreid. Bovendien
wordt een groot deel ervan in software afgehandeld. Het is uiteraard niet de bedoeling dat
iedereen die een USB driver schrijft dit opnieuw moet implementeren. Bovendien geeft dit
problemen wanneer meerdere drivers eenzelfde USB interface willen delen. In Linux is dit
probleem opgelost door de onderste lagen van het protocol door een module in de kernel
(de ‘USB core’) te laten afhandelen. De drivers voor de specifieke USB devices kunnen van
de daar geı̈mplementeerde functionaliteit gebruik maken via een gestandaardiseerde API.
De USB core maakt dan weer gebruik van een driver die specifiek is voor de gebruikte
host controller (bv. UHCI of EHCI).
De eerste belangrijke functie in de Linux USB API is usb register(struct usb driver
2 THE LINUX USB SUBSYSTEM
10
*drv). Deze moet worden aangeroepen wanneer de driver in het geheugen geladen wordt.
USB
Device Driver
USB
Device Driver
USB
Device Driver
Upper API
USB Core
Lower API
USB HC
Driver
USB HC
Driver
Figure 3: USB Core API Layers
Figuur 5.1: Structuur van het Linux USB subsysteem.
2
The Linux USB Subsystem
31
In Linux there exists a subsystem called “The USB Core” with a specific API to
support USB devices and host controllers. Its purpose is to abstract all hardware or device dependent parts by defining a set of data structures, macros
and functions.
The USB core contains routines common to all USB device drivers and host
Hiermee maakt hij zijn bestaan kenbaar bij het USB subsysteem. Het argument is een
structuur die onder meer het adres van een aantal door de driver geëxporteerde functies
bevat, het minor -nummer en een lijst van VID/PID combinaties van de devices waarvoor
de driver gebruikt kan worden.
Naast communicatie met het USB device wil een USB driver meestal ook communiceren
met een proces in user space. Hiertoe implementeert deze driver een ‘character device’.
Dit is een standaard interface in Linux die operaties voor het lezen en schrijven van data
per byte definieert. Vandaar ook de naam character device, dit in tegenstelling tot een
block device dat gegevens enkel in blokken van vaste grootte kan verwerken, zoals bv. een
harde schijf. Een character device wordt door de kernel herkend aan zijn major en minor
numbers. Elk type device heeft een vaste major en een bereik van minor nummers; een
specifiek device krijgt zijn eigen minor nummer. Voor seriële poorten bv. zijn major 4 en
het minor bereik 64–79 gereserveerd. Om de gebruiker niet met deze numerieke combinatie
te confronteren worden in het bestandssysteem een aantal snelkoppelingen gemaakt die
toelaten een meer betekenisvolle naam te gebruiken. Zo verwijst /dev/ttyS0 naar major 4,
minor 64 en stelt de eerste seriële poort voor. Het is niet toevallig dat een device voorgesteld
wordt door een bestand: voor de Linux API is er namelijk geen verschil tussen lezen uit
een device of uit een bestand. Ook o.m. sockets worden door een bestand voorgesteld. In
UNIX geldt immers “Everything is a file”.
Voor USB is major 180 gereserveerd. Een USB device driver kan een minor bereik
van 16 groot kiezen. Het kleinste minor nummer wordt opgenomen in de structuur die
doorgegeven wordt via usb register(); op deze manier kent de kernel alle major/minor
combinaties die gelden voor deze driver. Daar major en minor nummers slechts 8 bit
groot zijn kunnen zo slechts 16 verschillende USB drivers op één moment op hetzelfde
systeem aanwezig zijn. Bovendien zullen ze elk slechts één of twee devices beheren en is
een bereik van 16 dus eigenlijk veel te groot. Vanaf de (experimentele) kernel versie 2.5
kunnen deze minors dynamisch geassigneerd worden. Voor deze thesis werd steeds met
versie 2.4 gewerkt waar deze mogelijkheid nog niet op punt stond.
De driver voor een character device exporteert 5 belangrijke functies: read(), write(),
ioctl(), open() en release()1 . Met read() en write() kan het device gelezen en beschreven worden. Via ioctl() kunnen bepaalde opties ingesteld worden. Een aantal hiervan zijn gestandaardiseerd (bv. blocking of non-blocking calls2 ), de andere kunnen door de
ontwikkelaar van de driver zelf ingevuld worden. open() en release() worden gebruikt
1
In totaal zijn er 17 operaties (functies) gedefinieerd op een character device. Het is echter niet nodig
deze allemaal te implementeren. Sommige devices kunnen bijvoorbeeld enkel gelezen worden, in dat geval
is een write() functie zinloos.
2
Wanneer we uit een device willen lezen maar er is geen data beschikbaar zijn er twee opties. Ofwel
laten we de driver, en daarmee ook het gebruikersprogramma dat de oproep maakt, wachten totdat er wel
data beschikbaar is, dan hebben we een blocking call. Indien de leesfunctie direct terugkeert en het aan
het gebruikersprogramma overlaat om later opnieuw te proberen, dan is de oproep non-blocking.
32
om een device te openen en te sluiten. De toestand behorende tot een specifiek proces
kan hiermee geı̈nitialiseerd en verwijderd worden. Het kan bijvoorbeeld voorkomen dat
een device slechts door één proces geopend mag worden; een tweede oproep van open()
zal in dit geval falen. Ook het bijhouden van een lees-pointer, indien dit voor het device
van toepassing is, is enkel mogelijk indien de driver kan bijhouden welke oproep door welk
proces gemaakt wordt.
Een USB device is ‘hot pluggable’, dit wil zeggen dat de devices op elk moment in
het systeem gebracht kunnen worden en er ook weer uit verwijderd kunnen worden. De
ondersteuning hiervan loopt via twee extra functies: probe() en disconnect(). Wanneer
een nieuw device gedetecteerd wordt, zal de kernel voor elke driver de lijst van ondersteunde VID/PID combinaties afgaan. Indien deze overeenkomen, wordt voor deze driver
de probe() functie aangeroepen. De driver kan nu beslissen of hij eigenaar wordt van dit
device. Wanneer de kernel ziet dat de verbinding met het device verbroken wordt, zal de
disconnect() functie aangeroepen worden zodat de driver onder meer het geheugen kan
vrijgeven dat geassocieerd was met het device.
Voordat de driver uit het geheugen verwijderd wordt, moet deze usb deregister()
aanroepen om het USB subsysteem hiervan te verwittigen.
De driver kan met zijn device communiceren door gebruik te maken van een USB Request Block (URB). Deze structuur wordt gebruikt voor alle types van transfers (bulk,
iso, int of control) en voor beide richtingen (IN en OUT). Een nieuwe URB wordt verkregen door een oproep van usb alloc urb(). De driver zal vervolgens een aantal elementen van de structuur instellen zodat de core weet welke transfer verwacht wordt. De
belangrijkste ervan staan in tabel 5.1. Daarna kan de driver usb submit urb() aanroepen.
Hiermee wordt de communicatie gestart; de functie is echter asynchroon en retourneert
dus onmiddelijk. De driver kan nu het status element controleren. Zolang dit de waarde
-EINPROGRESS heeft is de transfer nog bezig. Een betere manier is het gebruik van
het element complete. Indien dit ingevuld wordt met het adres van een functie, de completion handler, zal deze door de kernel opgeroepen worden wanneer de transfer (al dan
niet succesvol) beëindigd is. Deze functie kan bv. een semafoor posten waarop de driver stond te wachten. Een URB die nog behandeld wordt, kan geannuleerd worden door
urb unlink urb() aan te roepen, bv. na een timeout.
5.2
Een USB Driver
De driver die in het kader van deze thesis geschreven werd, in de vorm van een kernel module, is te vinden als usb-driver/usb-fx2.c. Deze driver kan in principe gebruikt worden
voor meerdere types USB devices. De elementen die specifiek zijn aan de in paragraaf 4.3
besproken firmware werden in een hogere laag geplaatst (zie LibVNA, hoofdstuk 6).
Wanneer de module in het geheugen geladen wordt (d.m.v. insmod usb-fx2.o), zal de
33
Naam
dev
pipe
transfer buffer
transfer buffer length
actual length
complete
status
Doel
Pointer naar een structuur die het USB device beschrijft.
Geeft het type transfer aan (bulk/iso/int/control), de
richting (IN of OUT) en het endpoint nummer.
Adres van een stuk geheugen waar de te schrijven data
staat, of de ingelezen data moet komen.
Lengte van het geheugen op adres transfer buffer.
Aantal effectief gelezen of geschreven bytes.
Adres van een functie die opgeroepen wordt bij het
beëindigen van de transfer.
-EINPROGRESS zolang de transfer bezig is, daarna 0
voor succes of < 0 wanneer een fout optrad.
Tabel 5.1: Belangrijkste elementen van de URB structuur.
kernel eerst de functie usb fx2 init() aanroepen. Deze gebruikt usb register() om het
USB subsysteem ervan op de hoogte te brengen dat deze driver dient voor een device met
VID=0x04B4 en PID=0x8613. Deze combinatie wordt gebruikt voor een niet geprogrammeerde FX2, maar ook door de voorbeeldprogramma’s van Cypress die gebruikt werden
om de werking en snelheid van de driver te testen. Het was immers niet nodig dat de PC
een onderscheid kon maken tussen een FX2 met of zonder firmware, daar de firmware in
de gebruikte opstelling steeds manueel ingeladen werd. Wanneer men dit proces wil automatiseren is uiteraard een andere VID/PID combinatie nodig. De driver hoeft dan enkel
te reageren op een geprogrammeerd device. Het programmeren zelf kan gebeuren d.m.v.
de Hotplug module: dit is een verzameling scripts die bepaalde acties kunnen uitvoeren,
zoals het inladen van een driver of het programmeren van een device, telkens wanneer een
nieuw device gedetecteerd wordt.
De volgende belangrijke functie is fx2 probe(). Deze wordt door de kernel aangeroepen wanneer een device met de juiste VID/PID combinatie ingeplugd werd. De functie
krijgt als argument het adres van een structuur waarin alle descriptors opgeslagen liggen.
Er kan nu een minor geassigneerd worden aan het nieuwe device. Deze wordt gekozen uit
de 16 vaste minors van de driver. Ook wordt een structuur van het type usb fx2 gealloceerd, die dit device verder voor de driver zal voorstellen. Deze bevat een pointer naar de
structuur met descriptors en verder twee URB’s (één voor lezen en één voor schrijven),
hun bijbehorende buffergeheugens en een aantal instellingen. Vervolgens wordt de functie
fx2 set ep() aangeroepen. Deze maakt geen deel uit van de standaard interface maar is
een helperfunctie die enkel binnen de module van de driver gebruikt wordt. De driver is
zo opgezet dat de read() en write() functies elk op één specifiek endpoint werken. Het
doel van fx2 set ep() is dan ook om dit endpoint te kiezen. Hiervoor zal de lijst met
endpoint descriptors doorlopen worden, wanneer een geschikt endpoint gevonden is (met
34
de juiste richting, van het type bulk en eventueel met een specifiek endpoint nummer) zal
dit in de usb fx2 structuur opgenomen worden. Bovendien worden hier de URB’s en hun
buffers gealloceerd. De fx2 probe() functie laat fx2 set ep() het eerste geldige endpoint
kiezen, via ioctl() (zie verder) kan de gebruiker een specifiek endpoint kiezen.
Wanneer een gebruikersproces wil interageren met een device moet het dit eerst, net
zoals bij een bestand, openen. De gebruiker roept hiertoe de functie open() aan met als
argument de naam van een bestand dat het device voorstelt. Deze bestanden staan per
conventie in de directory /dev. De naam van een bestand is in principe vrij te kiezen; voor
de kernel is immers enkel de major/minor combinatie belangrijk. Het is echter beter een
naam vast te leggen zodat een gebruikersprogramma steeds de juiste driver kan vinden.
Voor deze FX2 driver werd gekozen voor het prefix fx2; hieraan wordt a toegevoegd voor
de eerste minor, b voor de tweede enz. De kernel zal vervolgens de functie fx2 open()
in de driver aanroepen. Deze krijgt onder andere het minor nummer van het gewenste
device mee en een file descriptor, dit is een pointer naar de structuur die het geopende
device verder zal beschrijven. Voor verdere toegangen tot het device is voor deze driver
enkel het minor nummer van belang. Alle instellingen worden gemaakt per device en
blijven dus actief wanneer het device gesloten wordt3 . De file descriptor heeft een speciaal
element, nl. private data, dat door de driver gebruikt kan worden. Hierin wordt een
pointer opgeslagen naar de usb fx2 structuur van het device zodat de driver later weet
welk device geopend was.
Nu het device geopend is, kan er gelezen en geschreven worden. Dit gebeurt uiteraard
met read() en write(). Deze functies gebruiken elk één specifiek endpoint, dat reeds
vroeger werd ingesteld d.m.v. fx2 set ep(). Beide functies hebben dezelfde argumenten:
de file descriptor, een pointer naar een databuffer buffer en het aantal bytes dat gelezen
of geschreven dient te worden, count. Er wordt ook een offset meegegeven, deze is echter
niet van belang daar het device enkel sequentieel toegankelijk is. Beide functies geven terug
hoeveel bytes er daadwerkelijk gelezen/geschreven zijn daar dit niet noodzakelijk gelijk is
aan het gevraagde aantal. Het is in dat geval aan de gebruiker om eventueel een nieuwe
oproep te maken die de rest van de data behandelt.
Alle data zit echter in virtueel geheugen horende bij het oproepende proces; de associatie met dit proces gaat verloren wanneer we dieper in de kernel gaan4 . We moeten de
data dus kopiëren naar ‘kernel geheugen’; dit is een stuk geheugen dat niet uitgeswapt kan
worden en dus voorgesteld kan worden door een fysiek adres dat in elke context geldig is.
3
In het algemene geval zal, telkens een device geopend wordt, een structuur gealloceerd worden die
meer informatie bevat. Zo zou het mogelijk zijn de gebruikte endpoints hierin op te slaan zodat men –
door het device vanuit verschillende threads te openen – uit meerdere endpoints tegelijk kan lezen.
4
De hier besproken driver draait binnen een context gerelateerd aan deze van het aanroepende proces,
en kent de virtuele geheugenlayout ervan. Wanneer een URB aan de kernel doorgegeven wordt zal deze pas
later verwerkt worden, bv. in de interrupthandler van de host controller. Deze draait in zijn eigen context
en heeft dus geen kennis van het proces dat aan de oorsprong lag van de URB.
35
Het is echter duidelijk dat fysiek geheugen een dure resource is. Het is bovendien slechts
betrouwbaar te verkrijgen in blokken van ten hoogste 128 KiB. De buffers behorende bij
een URB liggen in het gebied van het kernel geheugen en worden gealloceerd tijdens de oproep van fx2 set ep(). De grootte hiervan wordt bepaald door de constante MAX SIZE.
In een productie-driver zal deze constante gekozen worden om een zo hoog mogelijk debiet
te halen; hier werd deze vastgelegd op 64 KiB. Door de grootte van een read() aanvraag
aan te passen kunnen we zo de prestatie van URB’s van verschillende groottes onderzoeken. Eén URB kan dus nooit meer data verplaatsen dan MAX SIZE. Indien count groter
is, moeten we ofwel slechts een deel van de transfer doen en de gebruiker eventueel een
nieuwe oproep laten doen, of zelf het proces herhalen. De driver ondersteunt beide varianten. De keuze uit één van beide wordt bepaald door de instellingen read autorepeat
en write autorepeat.
Er werd gekozen om de oproepen van read() en write() synchroon5 te maken. Dit is
het eenvoudigste en bleek voldoende performant te zijn. De performantie van de huidige
implementatie en enkele verbeteringen hierop worden meer in detail besproken in paragraaf
5.3. De oproep van usb submit urb() is echter asynchroon. We moeten dus wachten totdat
de verwerking van de URB gedaan is. Hiertoe wordt de completion handler gebruikt. Deze
zal de hoofdthread wakker maken bij verandering van de status van de URB. Er wordt
gebruik gemaakt van een ‘wait-queue’; het principe hiervan is hetzelfde als dat van een
semafoor. Er is echter één probleem waarmee we rekening moeten houden. Het is namelijk
mogelijk dat de thread van de driver uitgescheduled wordt tussen het doorgeven van de
URB en het oproepen van de functie die de thread doet wachten op de completion handler.
De URB kan intussen afgewerkt worden, de completion handler zal zien dat de thread
niet slaapt en dus niets doen. Daarna gaat de thread verder, valt in slaap en zal daar
eeuwig blijven. Testen of de URB afgewerkt is vóór te slapen, helpt niet – de scheduler
kan immers nog steeds tussen de test en het begin van het slapen tussenkomen – tenzij
deze twee atomair uitgevoerd worden. Dit is wat er gebeurt in de wait event * macro’s.
Eerst wordt aangegeven dat de thread wacht op de completion handler. Vervolgens wordt
verhinderd dat de scheduler gestart wordt. Nu kan de test uitgevoerd worden. Indien de
URB niet afgewerkt is, wordt de scheduler alsnog gestart met schedule(). In het andere
geval wordt de scheduler terug geactiveerd en gaat de driver meteen verder. Indien de URB
nu zou completeren vlak na het uitvoeren van de test zal nog steeds schedule() uitgevoerd
worden. De driver had echter reeds laten weten dat hij wachtte op de uitvoering van de
completion handler. De scheduler zal zien dat dit reeds gebeurd is en de driver meteen
5
Een synchrone functie verricht het gevraagde werk en keert daarna pas terug. Het resultaat of een
statuswaarde worden teruggegeven en zijn dus meteen beschikbaar. Een asynchrone functie daarentegen
keert meteen terug, het werk wordt ‘op de achtergrond’ uitgevoerd. Indien de aanvrager een resultaat wil,
of wil weten of de aanvraag reeds afgewerkt is, moet dit later expliciet opgevraagd worden. Synchrone
functies zijn dus eenvoudiger om mee te werken; asynchrone functies laten echter toe om verschillende
taken tegelijk uit te voeren en zullen dus te verkiezen zijn wanneer performatie belangrijk is.
36
cmd
Omschrijving
arg
USB FX2 CONTROL
struct usb fx2 ioctl control
USB FX2 BULK
struct usb fx2 ioctl bulk
USB FX2 SET EP
endpoint nummer
USB FX2 GET EP
IN of OUT
USB FX2 GET EP INFO
struct usb endpoint descriptor
USB FX2 GET DEV INFO
struct usb fx2 device
USB FX2 SET OPTION
constante
Doe een transfer van het type control naar een willekeurig endpoint.
Doe een transfer van het type bulk naar een willekeurig endpoint.
Selecteer een nieuwe endpoint voor lezen (indien het
argument een IN endpoint beschrijft) of schrijven.
Retourneert het endpoint dat gebruikt wordt voor
lezen of schrijven.
Kopieert de endpoint descriptors naar de gegeven
structuur.
Kopieert een deel van de device descriptors naar de
gegeven structuur.
Laat toe de instellingen read autorepeat en
write autorepeat te wijzigen.
Tabel 5.2: Mogelijke opdrachten die via ioctl() gegeven kunnen worden.
laten verdergaan. Er zijn een aantal van deze macro’s gedefinieerd in de kernel header
bestanden. Een macro met timeout werd echter niet voorzien tot versie 2.5. Omdat een
timeout echter wel wenselijk is, werd de definitie van deze macro in de broncode van de
driver opgenomen zodat deze door fx2 read() en fx2 write() gebruikt kan worden.
De volgende functie uit de standaard interface is ioctl(). Deze is een verzameling
van alle functionaliteit van de driver die niet in standaard lees- of schrijfbewerkingen
uit te drukken is. fx2 ioctl() krijgt, naast de file descriptor, twee argumenten mee:
cmd en arg. cmd is een constante die gedefineerd wordt in usb-driver/usb-fx2.h, arg
is een argument en kan ofwel een geheel getal zijn, of het adres van een structuur indien
meerdere argumenten vereist zijn. De functie retourneert een geheel getal. Meer informatie
kan eveneens via een structuur met adres in arg teruggegeven worden. De verschillende
waarden voor cmd en hun betekenis worden weergegeven in tabel 5.2.
Tot slot moeten nog de functies fx2 release() en usb fx2 exit() geı̈mplementeerd
worden. De eerste wordt door de kernel opgeroepen wanneer de gebruiker het device sluit
met een oproep van close(). De laatste verwijdert de registratie van de driver bij de kernel
(d.m.v. usb deregister()) voordat de module uit het geheugen verwijderd wordt (bij
uitvoering van rmmod usb-fx2 op de command line of bij het afsluiten van het systeem).
Vermijden van races
Wanneer het device door verschillende processen geopend is, kan het zijn dat deze tegelijk
van het device gebruik proberen te maken. Daar de usb fx2 structuur gedeeld zal zijn
37
tussen deze twee processen kan dit leiden tot race-condities. Veronderstel bijvoorbeeld dat
proces A en proces B beide een pakket willen lezen. Proces A zal de fx2 read() functie
oproepen die de lees-URB in de usb fx2 structuur instelt op de voor proces A gevraagde
waarden. Voordat de URB aan de kernel doorgegeven kan worden, wordt proces A onderbroken en proces B gestart. Opnieuw wordt fx2 read() opgeroepen die nu diezelfde
URB zal instellen op de waarden voor proces B. Vervolgens wordt de URB door de kernel
verwerkt. Proces B heeft nu de gevraagde gegevens. Tenslotte zal proces A verdergaan.
Dit proces weet niet dat proces B ondertussen uitgevoerd is; en zal nietsvermoedend de
URB doorgeven aan de kernel. Het resultaat is dat de URB tweemaal met de parameters
van proces B uitgevoerd werd.
Het is daarom nodig de usb fx2 structuur te beschermen met een semafoor: enkel het
proces dat de semafoor in handen heeft – m.a.w. een pend-operatie heeft kunnen uitvoeren
– kan van de structuur gebruik maken, andere processen moeten wachten totdat het eerste
proces de semafoor gepost heeft.
Het is echter beter in iets meer granulariteit te voorzien. Met één semafoor voor de
hele structuur kan slechts één proces tegelijk een operatie uitvoeren. Stel bijvoorbeeld dat
we met twee processen werken: één dat constant data uitleest en één dat data schrijft. Bij
een niet al te hoog datadebiet zal het lezende proces meestal geblokkeerd staan binnenin
fx2 read() en de semafoor bezitten. Het schrijvende proces zal daarom bijna geen kans
hebben om het device te kunnen gebruiken. We kunnen dit probleem oplossen door verschillende semaforen te voorzien, die elk een deel van de structuur beschermen. In de hier
beschreven driver zijn dat er drie: één voor lezen die alle bulk in * elementen beschermt,
één voor de bulk out * elementen die bij schrijven gebruikt worden en een laatste voor
ioctl() operaties. Openen en sluiten van het device vereisen dat er geen uitstaande bewerkingen meer zijn en zullen dus de drie semaforen moeten bezitten. Ook wanneer via
fx2 ioctl() de functie fx2 set ep() aangeroepen wordt mogen uiteraard geen lees- of
schrijfbewerkingen bezig zijn.
5.3
5.3.1
Performantie
Structuur van het VIA moederbord
Om de prestaties van een device driver te kunnen plaatsen, wordt eerst een kort overzicht
gegeven van de hardware waarop deze draait. In figuur 5.2 is de structuur weergegeven
van het gebruikte EPIA-M moederbord; de meeste moderne PC moederborden hebben
een gelijkaardige structuur.
Bovenaan zien we de processor. Deze voert één bus naar buiten, nl. de Front Side Bus
(FSB). Over deze bus gebeurt alle communicatie met het geheugen en de randapparatuur.
De North Bridge (NB) vormt de interface tussen de FSB enerzijds en een aantal hogesnel-
38
Figuur 5.2: Structuur van het EPIA-M moederbord.
heidsbussen anderzijds. De belangrijkste hiervan is de geheugenbus (links), andere zijn de
AGP bus naar de grafische kaart en een link met de South Bridge (SB). De AGP bus is
hier niet naar buiten gevoerd, de grafische controller is immers geı̈ntegreerd in de North
Bridge.
De bus tussen North en South Bridge is meestal geen standaard bus, maar specifiek
39
voor de fabrikant van deze IC’s. In het geval van de EPIA-M wordt deze de V-Link bus
genoemd. Meestal is deze nauw verwant aan de PCI bus; de snelheid is echter hoger.
In de South Bridge worden een aantal I/O interfaces geı̈mplementeerd. Zo vinden we
meestal een IDE controller, een USB host controller en eventueel een ethernet interface.
Bovendien wordt hier de interface met de PCI bus gevormd. Conceptueel zijn IDE, USB en
ethernet controllers steeds componenten op de PCI bus. Doordat deze zich echter binnen
in de South Bridge bevinden is het mogelijk deze aan een hogere snelheid te laten werken
dan de externe PCI bus.
Een recente uitbreiding op deze structuur is het gebruik van een Low Pin Count (LPC)
bus. Vroeger werden een aantal lage snelheidsbussen zoals deze voor toetsenbord en muis,
en de oudere seriële en parallelle interfaces, geı̈mplementeerd in de South Bridge. Het
aantal pinnen van dit IC loopt hierdoor uiteraard snel op. De 4 bit brede LPC bus biedt
een oplossing voor dit probleem door het mogelijk te maken deze functies in een apart IC
te verzamelen.
5.3.2
Verwerking van gegevens in bulk
De doorvoersnelheid van USB 2.0 is gespecifieerd op 480 Mbit/s of 60 MB/s. De voor
een applicatie bruikbare snelheid is echter slechts 53 MB/s, daar het protocol een zekere
overhead heeft. Bovendien blijkt het met de huidige implementaties van host controllers
meestal niet mogelijk om voldoende data beschikbaar te hebben voor een continue transfer
aan de maximale snelheid.
De VIA VT8235 South Bridge bevat een geı̈ntegreerde USB host controller en wordt tot
één van de snellere implementaties gerekend. Toch kan deze gedurende één microframe, dit
is een vaste tijdseenheid van 125 µs, meestal slechts 10 pakketten van 512 bytes versturen.
In theorie kunnen dit er maximaal 13 zijn. Dit komt overeen met een nuttig debiet van zo’n
40 MB/s. Op het voor deze thesis gebruikte EPIA-M moederbord is eveneens deze host
controller gebruikt. De rest van het moederbord (en dan vooral de C3 processor) is echter
niet zo snel (de trade-off voor een lager verbruik moet zich uiteraard ergens manifesteren),
wat ervoor zorgt dat de bereikte snelheiden niet boven de 25 MB/s uitkomen. Het effectief
halen van dit maximum hangt uiteraard af van een aantal parameters. Deze worden hier
besproken.
De meest voor de hand liggende parameter die we kunnen veranderen is de grootte van
de data die in één URB verwerkt wordt. In figuur 5.3 is de doorvoersnelheid en de tijd om
de URB te behandelen weergegeven voor verschillende groottes van de URB databuffer. Er
wordt steeds één URB van de gegeven grootte aan de kernel doorgegeven. De tijd tussen
het oproepen van read() en het terugkeren ervan wordt opgemeten. De doorvoersnelheid
wordt vervolgens berekend als
#bytes
tijd .
Het eerste wat opvalt, is dat de tijd steeds een
veelvoud is van 125 µs. We kunnen dus hoogstens eenmaal per microframe een nieuwe
40
Schrijfsnelheid bij USB 2.0
25
4
3
15
2
10
Tijd (ms)
Doorvoersnelheid (MiB/s)
20
1
5
Snelheid
Tijd
0
0
0
8
16
24
32
40
URB grootte (KiB)
48
56
64
Leessnelheid bij USB 2.0
25
4
3
15
2
10
Tijd (ms)
Doorvoersnelheid (MiB/s)
20
1
5
Snelheid
Tijd
0
0
0
8
16
24
32
40
URB grootte (KiB)
48
56
64
Figuur 5.3: Doorvoersnelheid van één URB bij variërende grootte.
URB starten. In een microframe passen maximum 13 pakketten of 6,5 KiB. Een URB
die net één byte groter is, zal dus twee volledige microframes in beslag nemen. Hieruit
volgt dat een URB van net 6,5 KiB de hoogste doorvoersnelheid zou opleveren. De host
controller haalt meestal echter minder dan 13 pakketten per microframe. Bovendien kan
ander verkeer op de USB bus deze waarde nog beı̈nvloeden. Het is daarom niet mogelijk het
laatste microframe van een URB steeds goed te vullen. Het lijkt dan ook het beste om de
41
Schrijfsnelheid bij USB 2.0 en kernel versie 2.5
25
4
3
15
2
10
Tijd (ms)
Doorvoersnelheid (MiB/s)
20
1
5
Snelheid
Tijd
0
0
0
8
16
24
32
40
URB grootte (KiB)
48
56
64
Leessnelheid bij USB 2.0 en kernel versie 2.5
25
4
3
15
2
10
Tijd (ms)
Doorvoersnelheid (MiB/s)
20
1
5
Snelheid
Tijd
0
0
0
8
16
24
32
40
URB grootte (KiB)
48
56
64
Figuur 5.4: Doorvoersnelheid van één URB bij variërende grootte voor nieuwere kernel
versies.
URB’s zo groot mogelijk te maken. Op deze manier kunnen we een schrijfsnelheid bereiken
van 20 MiB/s (dit komt overeen met 5 pakketten per microframes). De leessnelheid heeft
een maximum op 24 MiB/s (dit zijn 6 pakketten per microframe). Uit de metingen blijkt
tevens dat elke URB hier bovenop nog eens 125 µs extra tijd nodig heeft. Dit is weer een
reden om een URB groot te maken, deze tijd wordt zo verspreid over een groot aantal
42
bytes. In nieuwere versies van de kernel (vanaf 2.4.21 en in de 2.5 serie) bleek echter dat
deze extra tijd verdwenen was (zie figuur 5.4). Dit zorgt voor sterk verbeterde prestaties
bij kleine URB’s. De maximum snelheid blijft gelijk, al wordt die bij een kleinere URB
reeds bereikt. De tijd blijft een geheel aantal microframes, zodat een grotere URB nog
steeds voordeel biedt.
Er is echter een bovengrens op de grootte van een URB. De data moet namelijk in
kernel geheugen liggen; dit wordt gealloceerd d.m.v. een oproep naar kmalloc(). Deze
functie zal geen blokken teruggeven die groter zijn dan 128 KiB. Bovendien stijgt de
snelheid nog nauwelijks wanneer we de URB groter maken dan ongeveer 16 KiB (bij de
nieuwere kernels). De leessnelheid lijkt zelfs licht te dalen.
De data die voor deze testen gebruikt werd, stond reeds in het geheugen en moest
dus niet met een andere systeemoproep gelezen of geschreven worden. In een realistische
situatie zal dit echter wel het geval zijn. De processor en PCI bus zullen dus gedeeld
moeten worden tussen de USB interface en bv. een IDE controller bij streaming naar hard
disk. Dit zal de doorvoersnelheid uiteraard negatief beı̈nvloeden. Ook zijn de opgemeten
tijden gemiddelden: een leessnelheid van 20 MiB/s zegt niets anders dan dat we in één
gemiddelde seconde 20 MiB kunnen verwerken. Dit kan als één constante stroom gedurende
deze seconde, maar ook in één burst die slechts 50 % van de tijd in beslag neemt, gevolgd
door een pauze van 0,5 seconden. Indien het device in voldoende buffering voorziet en de
data geen real-time karakter hebben, is dit geen probleem. Wanneer deze buffering beperkt
is, of we enige eisen stellen aan de maximale vertraging die de doorvoer introduceert,
moeten we extra factoren in beschouwing nemen.
5.3.3
Verwerking van gegevens met real-time eisen
Om het real-time gedrag van de driver op te meten in een realistische situatie, werd een
testopstelling opgezet die toelaat om de ADC’s te karakteriseren. De gebruikte ADC levert
2,5 MSPS van 16 bit af. De FPGA leest deze woorden uit en schrijft ze per pakket van
512 bytes in een FIFO van de FX2. Dit gebeurt dus aan een snelheid van 5 MB/s. Dit
is merkelijk kleiner dan de maximale snelheid van 24 MiB/s die de driver kan halen en
zou dus geen probleem mogen vormen. Het kan echter gebeuren dat er zich een pauze
voordoet in de USB transfer. In dit geval zal de data voorlopig opgeslagen worden in de
(als viervoudig uitgevoerd ingestelde) buffers van de FX2. Indien de ‘full flag’ van de FIFO
actief is terwijl de FPGA nieuwe gegevens moet schrijven, is er data verloren gegaan. Deze
conditie is gemakkelijk in te stellen als trigger op een digitale oscilloscoop. We kunnen op
deze manier zien of er data verloren gaat. Het blijkt nu dat telkens wanneer de harde schijf
gebruikt wordt, een groot aantal pakketten verloren gaan. Wanneer het uitleesprogramma
uitgevoerd is als één thread die afwisselend leest en schrijft, kan er uiteraard geen USB
transfer plaatsvinden tijdens het schrijven naar de harde schijf. Een eerste poging tot
43
oplossing bestaat dan ook uit het opsplitsen van het programma in twee threads: één
thread die de data over USB uitleest en in het geheugen plaatst en een tweede thread
die de data uit het geheugen naar de harde schijf schrijft. Deze tweede thread mag nu
blokkeren terwijl de harde schijf actief is; de eerste thread kan steeds blijven uitlezen. Het
blijkt echter dat dit het probleem niet oplost. De pauze in de USB transfer blijkt dus niet
te komen van een geblokkeerde thread, maar wel van een overbelasting van de CPU of de
PCI bus. De limiet van het VIA-bordje blijkt dus bereikt te zijn.
We hebben echter niet zoveel data nodig (slechts enkele MiB) en kunnen dus kiezen
alle data in het geheugen op te slaan en pas na het inlezen de harde schijf te gebruiken. De
volgende parameter is opnieuw de grootte van de gebruikte URB’s. Een op het eerste zicht
verrassend effect is dat grote URB’s het hier helemaal niet zo goed doen. Het optimum
ligt nu rond de 8 KiB. Grotere URB’s introduceren vrij snel veel grotere verliezen6 .
Het bekijken van deze outputpin geeft uiteraard slechts een kwalitatief beeld. Om
kwantitatief het verlies te kunnen bepalen werd de firmware van de FX2 aangepast, zodat
deze met een bepaald interval een pakket produceert. Elk pakket wordt voorzien van een
oplopend nummer. De PC leest nu 2048 pakketten in. Door de nummering te controleren
kan bepaald worden hoeveel pakketten verloren gegaan zijn. Wanneer we dit doen voor
verschillende groottes van de gebruikte URB’s en een aantal intervallen tussen de pakketten, bekomen we figuur 5.5. We wisten al dat kleine URB’s (1 KiB en kleiner) niet
het vereiste debiet halen en dus pakketten laten vallen. Voor grote URB’s (vanaf 16 KiB)
stijgt het verlies opnieuw.
Wanneer we de architectuur van de driver bekijken, is dit echter vrij eenvoudig te
verklaren. De sequentie om een hoeveelheid data te lezen, is immers de volgende:
1. Het proces (dat in user-space draait) doet een oproep van read().
2. De context wisselt van user-space naar kernel-space.
3. De kernel roept de functie fx2 read() van de driver aan.
4. De driver geeft een URB door aan de kernel d.m.v. usb submit urb() en wacht tot
de completion handler opgeroepen wordt.
5. De kernel zorgt ervoor dat de URB afgehandeld wordt.
6. De kernel roept de completion handler van de driver op.
7. De driver gaat verder: de ingelezen data wordt gekopieerd naar de buffer van de
gebruiker en fx2 read() keert terug.
6
In feite zou men voor real-time data beter transfers van het isochronous type gebruiken. Het probleem
dat de ingelezen data snel genoeg verwerkt moet worden, blijft echter bestaan, en kan voor iso transfers
op dezelfde manier opgelost worden als verder voor bulk beschreven zal worden.
44
Pakketverlies
100 %
22 µs
35 µs
69 µs
Verlies
10 %
1%
0
1
4
16
64
URB grootte (KiB)
Figuur 5.5: Percentage van pakketten dat verloren gaat in functie van de grootte van de
gebruikte URB’s en de tijd tussen twee pakketten.
8. De context wisselt van kernel-space naar user-space.
9. Het proces verwerkt de data en gaat terug naar 1.
Om een idee te krijgen van de duur van de verschillende stappen werd de driver aangepast, zodat deze na de belangrijkste stappen de huidige tijd naar buiten voert7 . Het
resultaat hiervan is te vinden in tabel 5.3. We zien dat de grootste tijd in beslag genomen
wordt door het verwerken van de URB door de kernel. Op de tweede plaats komt het kopiëren van de data uit de URB buffer naar een buffer in de geheugenruimte van het proces.
De tijd tussen het oproepen van de completion handler (stap 6) en het verdergaan van de
driver (stap 7), Wake-up in de tabel, en het verblijf in user-space (inclusief het wisselen
van de context tussen kernel- en userspace, stappen 1, 2, 8 en 9) zijn verwaarloosbaar.
Het enige moment waarop de USB interface actief is, is tijdens stap 5. De som van alle
tijden voor de andere punten geeft dus al een ondergrens voor de tijd waarop helemaal
7
Het naar buiten voeren van debug informatie uit de driver kan gebeuren door een entry te plaatsen in
de buffer met kernel boodschappen (d.m.v. de printk() functie). Het gebruik van deze functie kan echter
tot gevolg hebben dat de scheduler oproepen wordt en kan dus leiden tot het starten van een andere thread.
Dit zal de meetresultaten uiteraard grondig verstoren. De correcte werkwijze is dus om de tijden in een
datastructuur van de driver op te slaan en deze pas naar buiten te brengen nadat de meting afgelopen is.
45
URB grootte (KiB)
Transfer (µs)
Kopiëren (µs)
Wake-up (µs)
User space (µs)
Totaal (µs)
1
115 (92%)
5 (4%)
4
1
125
4
223 (90%)
21 (8%)
4
1
249
16
662 (88%)
83 (11%)
4
1
750
64
2245 (82%)
494 (18%)
4
6
2749
Tabel 5.3: Tijdsverdeling in de driver.
geen USB activiteit kan plaatsvinden. Wetende dat het kopiëren bij URB’s van 16 KiB
reeds 83 µs in beslag neemt – op deze tijd is de volledige bufferruimte van de FX2 gevuld
wanneer de tijd tussen twee pakketten 22 µs bedraagt – is het duidelijk vanwaar de plotse
daling in prestatie zijn oorsprong vindt.
Dit probleem is op te lossen door een soort van pipelining toe te passen: we kunnen met
twee URB’s werken, waarvan er steeds één zich in stap 5 bevindt. Ondertussen kunnen we
de data van de andere verwerken. Dit vereist echter een grondige aanpassing van de driver
(inclusief omvorming naar een asynchrone read() functie). Er werd dan ook geen verder
onderzoek in deze richting gedaan. Bovendien hebben we de karakterisering van de ADC’s
kunnen uitvoeren zonder deze aanpassing en zal het datadebiet voor de afgewerkte VNA
enkele grootte-ordes lager liggen, zodat een meer performante driver ook hier niet nodig
is.
5.3.4
Uitlezen van gegevens met beperkte latentie
Een ander probleem stelt zich bij het uitlezen van de door de FPGA gegenereerde samples.
We weten op voorhand niet hoeveel volle pakketten zich reeds in de buffers van de FX2
bevinden. We willen deze op een gegeven moment allemaal uitlezen en de read() oproep
meteen laten terugkeren, dit om de samples zo snel mogelijk aan de gebruiker te kunnen
doorgeven. De eenvoudigste oplossing is om data op te vragen ter grootte van één pakket.
Indien dit pakket er is, kunnen we het direct verwerken en de oproep herhalen. Wanneer
geen nieuwe data aanwezig is, moeten we toch wachten. Wanneer we echter de performantie
hiervan bekijken kunnen we op figuur 5.4 zien dat we op deze manier hooguit 4 MiB/s
kunnen halen. We kunnen echter geen read() oproep doen voor een grootte van meer
dan één pakket. Indien deze extra pakketten er niet zijn, zal read() blokkeren totdat er
nieuwe samples zijn. Een niet-blokkerende versie is niet beschikbaar. Het USB subsysteem
laat immers niet toe om de hoeveelheid beschikbare data uit te lezen. Dit zou eventueel in
firmware geı̈mplementeerd kunnen worden en via het EP1 endpoint worden opgevraagd,
maar dit vereist dan weer twee extra USB transfers, wat het voordeel van een hoger debiet
teniet zou doen.
Het werken met meerdere URB’s blijkt hiervoor een oplossing te zijn. Deze URB’s,
46
Leessnelheid met URB queueing
25
Doorvoersnelheid (MiB/s)
20
15
10
5
0
1
2
4
8
16
32
64
128
256
Aantal URB’s
Figuur 5.6: Performantie van URB queueing voor een verschillend aantal URB’s.
elk met een grootte van één pakket, kunnen tegelijk aan de kernel doorgegeven worden.
Wanneer één pakket ontvangen is, zal de completion handler één keer opgeroepen worden.
De betreffende data kan dan naar de gebruiker doorgegeven worden (ook dit vereist een
verandering in het gedrag van de read() functie). De overige URB’s blijven in een wachtrij
staan (vandaar de naam van dit principe: URB queueing) totdat ook voor hen een pakket
beschikbaar is. Een URB waarmee pas een pakket ingelezen werd, kan terug achteraan de
wachtrij geplaatst worden, zodat steeds een vast aantal URB’s beschikbaar is. Wanneer
meerdere pakketten tegelijk beschikbaar komen, zullen evenveel URB’s verbruikt worden.
We kunnen op deze manier een variabele doorvoersnelheid opvangen en toch de latentie
beperken. Indien voldoende URB’s gebruikt worden (een honderdtal), is ook hier een
snelheid van 20 MiB/s haalbaar (zie figuur 5.6).
47
Hoofdstuk 6
LibVNA
6.1
Inleiding
Om de VNA te besturen vanuit een proces op het VIA moederbord, moeten opdrachten
gegeven worden aan de FX2. We hebben al een driver, zodat we vanuit van user space
met de FX2 kunnen communiceren; en in de firmware van de FX2 werden een aantal
opdrachten gedefinieerd waarmee de externe interfaces, nl. de FIFO’s, de I2 C bus en de
seriële poorten, te controleren zijn. Er zijn echter een aantal stappen nodig om op deze
manier een volledige communicatiecyclus te doorlopen. Als voorbeeld wordt de sequentie
gegeven voor het lezen van een aantal bytes via de I2 C bus. Andere opdrachten zijn
analoog. Veronderstel dat count het aantal te lezen bytes bevat en address het adres:
1. Reserveer een buffer van count + 2 bytes, noem deze buffer.
2. Stel buffer[0] = I2C READ, buffer[1] = address en buffer[2] = count.
3. Verstuur buffer over EP1OUT. Hiertoe dient een structuur van het type struct
usb fx2 ioctl bulk ingevuld te worden met .endpoint = EP1OUT, .data =
buffer, .size = 3 en een timeout waarde. Het adres van deze structuur wordt aan
de driver doorgegeven als argument van een ioctl() oproep, het cmd veld hiervan
is USB FX2 BULK.
4. Vraag het resultaat op via EP1IN. Dit is hetzelfde als stap 3, maar .endpoint =
EP1IN en .size = count + 2.
5. Het resultaat van de bewerking (OK of een I2 C specifieke fout), het effectief aantal gelezen bytes en de data zelf kunnen nu gevonden worden in resp. buffer[0],
buffer[1] en vanaf buffer[2].
Voor een gebruiker is dit uiteraard niet aanvaardbaar. Hij wil een oproep kunnen
doen van de vorm result = vna i2c read(address, count, &data). Bovendien zijn
48
deze mogelijkheden specifiek voor de gebruikte firmware, maar niet voor het feit dat deze
voor de VNA gebruikt wordt. Daarom werd besloten om een extra laag tussen te voegen
die functies zoals vna i2c read() implementeert. Via deze laag, LibVNA genaamd, kan
een gebruikersprogramma rechtstreeks de externe interfaces van de FX2 besturen. Deze bibliotheek kan gebruikt worden in andere projecten met de FX2. Het gebruikersprogramma
dat de VNA zal besturen is nu onafhankelijk van het feit dat een USB interface gebruikt
werd. In dit opzicht is het structuurschema van figuur 3.2 dus om te vormen naar een
nieuwe structuur. Deze staat weergegeven in figuur 6.1.
6.2
Interface
De interface van deze LibVNA module bestaat uit 14 functies. Met vna open() kan een
device aan de hand zijn naam (per conventie van de vorm /dev/fx2? met ? = a .. d)
geopend worden. Deze functie werkt zoals een gewone open() oproep en geeft een file
descriptor terug. Alle andere functies gebruiken deze descriptor voor verdere communicatie
met het device. De prototypes en een beschrijving van de argumenten van deze functies is
te vinden in appendix A.
49
WebVNA
read()
write()
vna i2c *()
vna com send()
LibVNA
USB FX2 driver
FX2
I2 C
FIFO
FPGA
...
USART
Z8
Figuur 6.1: Structuur van de verschillende componenten: de combinatie LibVNA + USB
driver + FX2 firmware is transparant.
50
Hoofdstuk 7
WebVNA
7.1
Inlezen van samples
De bovenste laag van de software is de WebVNA component. Deze verzorgt de verbinding
tussen de gebruikersinterface – die via TCP/IP communiceert – en de eigenlijke hardware,
die d.m.v. de reeds besproken componenten aangesproken kan worden. WebVNA draait
als een proces in user space en heeft een aantal threads, zodat de verschillende functies
parallel uitgevoerd kunnen worden.
De eerste taak is het inlezen van samples uit de FPGA. Wanneer amplitude en fase
opgemeten zijn voor een bepaald frequentiepunt, zal de FPGA een datapakket in de FIFO
van de FX2 schrijven. Dit pakket bestaat uit 7 16 bit woorden: een volgnummer zodat we de
frequentie kunnen achterhalen waarbij deze meting gedaan is, en de amplitude en fase van
beide meetsignalen en het referentiesignaal. Het inlezen gebeurt door continu te proberen
een pakket van de FX2 uit te lezen. Dit zal meestal niet mogelijk zijn, zodat de read()
functie blokkeert. Om de rest van het proces niet mee te blokkeren, wordt dit gedaan in
een afzonderlijke thread. De betreffende code is te vinden in webvna/vna data.c.
De gegevens van de FPGA zijn in 16 bit integer formaat. Er moet echter nog enige
verwerking op plaatsvinden. Om hierbij geen precisie te verliezen, wordt eerst overgegaan
op floating point formaat. Dan moeten de reflectie- en transmissiecoëfficiënten berekend
worden. De FPGA levert deze waarden af in relatie tot het referentiesignaal X. We kunnen
de parameters van de transfertfunctie H dus berekenen uit het uitgangssignaal Y via:
(
⇒
Ah =
φh
Ay
Ax
= φy − φx
51
(7.1)
7.2
Locking
In het proces wordt een datastructuur bijgehouden die de huidige instellingen van de VNA
weergeeft, alsook de laatste meting voor elk frequentiepunt. Deze structuur is gedeeld
tussen de verschillende componenten, te weten die voor het inlezen van de samples, het
aanpassen van de instellingen en het uitlezen van samples voor weergave aan de gebruiker.
Omdat we niet willen dat een gelijktijdige toegang tot de structuur door twee verschillende
threads met elkaar interfereert, moeten we dus in een mechanisme voorzien zodat op elk
moment slechts één thread toegang krijgt.
Het blijkt echter dat dit een te grote restrictie is. Het is namelijk geen probleem dat twee
verschillende threads tegelijk uit de structuur lezen. Er is enkel een probleem wanneer twee
threads tegelijk schrijven, of wanneer er één leest en een andere schrijft. Een mechanisme
dat dit principe kan implementeren is een read-write lock. Dit is een object dat lijkt op
een semafoor, maar twee sets ‘pend’ en ‘post’ operaties heeft: één voor lezen en één voor
schrijven. Zolang er geen enkele thread een write lock heeft, kunnen nieuwe threads read
locks aanvragen, wanneer er één thread een write lock bezit, kan niemand nog lezen, noch
een tweede write lock aanvragen. Indien een write lock aangevraagd wordt terwijl er nog
read locks openstaan moet de aanvrager van de write lock uiteraard blokkeren. Het is best
om vanaf dan geen nieuwe read locks meer toe te kennen omdat het anders te lang kan
duren voordat alle read locks losgelaten zijn en de write lock toegekend kan worden.
7.3
Besturing van de hardware
Wanneer de gebruiker de opdracht geeft om andere frequentiepunten te gebruiken zal
hiertoe een opdracht naar de FPGA verstuurd worden. Dit gebeurt door deze parameters
in één pakket over EP2OUT te sturen.
In het analoge gedeelte zijn eveneens twee componenten die door de gebruiker bestuurd
kunnen worden: een schakelbare attenuator en een crossbar. Beide componenten worden
gestuurd door elk één digitaal signaal. Hiervoor worden de outputpinnen van een Z8 processor gebruikt. Deze Z8 staat via een seriële poort in verbinding met de FX2. Omdat
slechts 2 bits (het niveau van beide outputpinnen) ingesteld moeten worden, kan dit door
het versturen van slechts één byte. Het niveau van beide pinnen staat in de laagste twee
bits. Er is geen nood aan communicatie in de andere richting of transmissie van meerdere
bytes kort na elkaar. Dit verklaart het ontbreken van uitgebreidere communicatieroutines
in LibVNA.
52
Opdracht
Argumenten
FREQ SET
f min f max f inc
ATTN SET
waarde
CB SET
waarde
UDP START
ip port
UDP STOP
Beschrijving
Stelt de gebruikte frequentiepunten in: van f min tot f max
in stappen van f inc.
Schakelt de attenuator aan of uit.
Stelt de crossbar in voor het meten van S11 en S21 dan wel
S22 en S12 .
Start het streamen van samples naar de host op IP adres ip
en UDP poort port.
Stopt het streamen van samples.
UDP FEEDBACK
last seq ploss X recv
TCP DUMP
CMD QUIT
Geeft informatie terug over de kwaliteit van de ontvangen
UDP stroom. Deze wordt gebruikt voor de bandbreedtecontrole, zie paragraaf 7.5.1.
Verstuurt de opgemeten data over de TCP verbinding, zie
paragraaf 7.5.2.
Beëindigt de verbinding.
Tabel 7.1: Opdrachten voor WebVNA.
7.4
Interactie met de gebruiker
Het programma dat de gebruikersinterface implementeert (verder kortweg client genoemd)
begint met een TCP connectie op te zetten naar het VIA moederbord. Hiertoe is poort 99
gekozen. WebVNA luistert op deze poort. Telkens wanneer een client hiermee verbinding
maakt, wordt een nieuwe thread gestart. Het is niet de bedoeling dat meerdere client
programma’s tegelijk proberen de VNA te besturen – dit leidt uiteraard tot conflicten
– de TCP verbinding kan echter ook gebruikt worden voor het overdragen van samples,
zie hiervoor paragraaf 7.5.2. Over de TCP verbinding worden door de client opdrachten
gegeven aan WebVNA. Elke opdracht bestaat uit een 32 bit code en eventueel een aantal
parameters. De verschillende opdrachten en hun beschrijving is te vinden in tabel 7.1.
7.5
7.5.1
Overdracht van samples naar de gebruiker
Streaming van data in real-time
Karakteristieken van de datastroom
Als we de VNA willen gebruiken om bv. een filter in te stellen, is het noodzakelijk dat het
gehele frequentiespectrum een voldoend aantal keer per seconde opgemeten wordt. Bovendien mag er niet teveel vertraging optreden tussen het opmeten en het weergeven van een
53
reeks metingen. Het systeem filter – VNA – gebruiker bevat immers een terugkoppeling;
indien er zich teveel vertraging voordoet in dit systeem kan instabiliteit optreden wat het
correct instellen van het filter sterkt bemoeilijkt.
Voor de stroom van meetgegevens van de VNA naar de gebruiker stellen we daarom
volgende eisen:
• Tot 20 beelden per seconde,
• 512 frequentiepunten per beeld,
• een maximale vertraging tussen opmeten en weergeven van 150 ms.
De meetwaarden voor één frequentiepunt bestaan uit 4 vlottende kommagetallen van 32
bit (reflectie- en transmissiemeting, elk een amplitude- en fasecomponent). Het debiet zal
dus maximaal 1,25 Mibit/s bedragen.
Keuze van een protocol
We kunnen veronderstellen dat niet lang na het meten bij een gegeven frequentiepunt
een nieuwe meting gedaan wordt van datzelfde punt. Wanneer de waarde van deze eerste
meting door de client niet of niet correct ontvangen wordt, is het daarom niet nuttig
hiervan een kopie door te sturen; deze wordt immers meteen overschreven door een meer
actuele waarde. We hebben met andere woorden geen retransmissiemechanisme nodig.
Wanneer we dit mechanisme toch zouden gebruiken zal het voorvallen dat een verloren
meting opnieuw verstuurd wordt; alle volgende metingen moeten hierop echter wachten.
Retransmissie zal dus verhinderen dat we onder de maximale vertraging van 150 ms blijven.
Het TCP protocol voorziet in retransmissie en is dus niet geschikt.
Indien we het Internet willen gebruiken, hebben naast TCP slechts één andere keuze:
UDP. Dit protocol ondersteunt enkel een onbetrouwbare punt-tot-punt transfer. Indien we
verdere functionaliteit willen, moeten we die zelf toevoegen. Hierbij valt in het bijzonder
bandbreedtecontrole te vernoemen. Het gaat immers niet op om dezelfde hoeveelheid data
te versturen over een modem-verbinding als over een ethernet. Men zou de keuze hiervan
aan de gebruiker kunnen laten, maar deze weet dit niet altijd. Bovendien kan er ander
(variabel!) verkeer op het netwerk zijn zodat een continue en automatische aanpassing van
het gebruikte debiet noodzakelijk is.
Bandbreedtecontrole met TFRC
Het blijkt dat dit probleem zeer algemeen is. Bij telefonie over het Internet bijvoorbeeld
gelden dezelfde eisen: een slecht ontvangen hoeveelheid spraak moet niet opnieuw verstuurd worden: deze komt toch te laat en zal volgende blokken ophouden. TCP is dus
54
ook hier niet bruikbaar. Toch is een vorm van bandbreedtecontrole (die wel in TCP ingebouwd is) wenselijk om voor elke netwerksituatie een zo goed mogelijke kwaliteit te
kunnen aanbieden. Ook online games hebben hetzelfde probleem. Bijgevolg zorgen specifieke applicaties elk voor hun eigen principe van flow control. Het is echter niet eenvoudig
om dit goed te doen. Bovendien is het niet nodig om voor elke applicatie opnieuw hetzelfde
werk te doen. Daarom wordt binnen het Internet Engineering Task Force, het orgaan dat
verantwoordelijk is voor de protocollen die op het Internet gebruikt worden, getracht tot
een nieuw protocol te komen. Dit zal als alternatief voor TCP en UDP dienen en de flow
control van TCP combineren met de lage overhead en latenties van UDP (en dus geen
betrouwbare ontvangst garanderen). Een van de protocollen die in deze context voorgesteld worden is het Datagram Congestion Control Protocol (DCCP). In de toekomst zal
men waarschijnlijk een standaard protocol kunnen gebruiken. Voorlopig is dit nog in geen
enkel besturingssysteem geı̈mplementeerd (de specificatie is immers nog niet definitief) en
moet dus met een eigen oplossing gewerkt worden.
Ook op dit vlak is er echter vooruitgang. Het TCP Friendly Rate Control protocol
(TFRC, [RFC3448]) voorziet in een protocol voor bandbreedtecontrole. De implementatie,
o.a. het pakketformaat en het type verbinding waarover de feedback teruggestuurd wordt,
wordt aan de uitvoerder overgelaten. Het protocol steunt op twee types transfer: de stroom
van data van de server naar de client, en een stroom in de andere richting die feedback
levert over hoe de datastroom ontvangen wordt. In deze implementatie loopt de datastroom
over UDP, de feedback wordt over de reeds opgezette TCP verbinding gestuurd. Via de
feedback worden de round trip time (RTT), het pakketverlies (ploss) en het werkelijk door
de client ontvangen debiet (X recv) opgemeten. Het pakketverlies en het ontvangen debiet
kunnen door de client eenvoudig opgemeten worden en meteen in een feedbackbericht
opgenomen worden. De bepaling van de round trip time gebeurt best in de server. Hiertoe
stuurt de client enkel het identificatienummer van het laatst ontvangen pakket naar de
server. Deze weet wanneer dit verstuurd werd. De tijd tussen twee pakketten is immers
constant, zodat ook de RTT bepaald kan worden. Op basis van deze parameters wordt
een nieuw zenddebiet bepaald. Een pakket heeft een constante grootte en bevat telkens
een heel beeld met alle frequentiepunten. Hieruit kan berekend worden hoeveel van deze
pakketten per seconde verstuurd mogen worden.
Performantie
Een algoritme voor bandbreedtecontrole wordt op twee punten beoordeeld: enerzijds het
goed gebruiken van het beschikbare debiet, en anderzijds het evenredig verdelen van dit
debiet over de verschillende connecties die ervan gebruik willen maken. Het gaat dus niet op
om zoveel mogelijk data over het netwerk te pompen. Andere gebruikers dienen eveneens
hun deel te krijgen. TCP lost dit zeer goed op zoals te zien is in figuur 7.1. Hiertoe werden
55
25
Totaal (kB/s)
20
15
10
5
0
25
TCP 1 (kB/s)
20
15
10
5
0
25
TCP 2 (kB/s)
20
15
10
5
0
0
20
40
60
Tijd (s)
80
100
120
Figuur 7.1: Verdeling van het beschikbare debiet tussen twee TCP verbindingen.
twee TCP verbindingen opgestart over een netwerk dat in totaal 20 kB/s aankan. Het
debiet van beide connecties werd uitgezet in functie van de tijd. De eerste verbinding
werd gestart op tijdstip 0. Na ongeveer 20 seconden werd de tweede verbinding opgezet.
Er is te zien dat de eerste verbinding een deel van zijn debiet opgeeft te voordele van
de tweede. Uitgemiddeld over een tiental seconden wordt de beschikbare bandbreedte nu
gelijk verdeeld over de beide verbindingen. Op 110 seconden werd de eerste verbinding
beëindigd, de tweede verbinding neemt nu na enkele seconden het hele debiet over.
56
25
Totaal (kB/s)
20
15
10
5
0
25
TCP (kB/s)
20
15
10
5
0
25
TFRC (kB/s)
20
15
10
5
0
0
10
20
30
40
Tijd (s)
50
60
70
80
Figuur 7.2: Verdeling van het beschikbare debiet tussen een TCP en een TFRC verbinding.
Omdat TCP op dit vlak goed scoort en een nieuw protocol in veel gevallen zijn bandbreedte zal moeten delen met een TCP verbinding, wordt ter bepaling van de kwaliteit van
een nieuw procotol voor bandbreedtecontrole de ‘TCP friendlyness’ als parameter gekozen.
Hiertoe voert men het vorige experiment opnieuw uit maar neemt het nieuwe protocol de
plaats in van één van de TCP verbindingen. Het resultaat voor de hier gebruikte TFRC
implementatie is te zien is in figuur 7.2.
Hoe het protocol reageert op een variërende netwerksituatie is weergegeven in figuur
57
Gebruikt debiet
8
ontvangen
beschikbaar
7
Bandbreedte (kB/s)
6
5
4
3
2
1
0
0
10
20
30
Tijd (s)
40
50
60
50
60
Round trip time
4
3.5
Round Trip Time (ms)
3
2.5
2
1.5
1
0.5
0
0
10
20
30
Tijd (s)
40
Figuur 7.3: Gebruikt debiet en RTT bij variërende netwerkcondities.
7.3. Op tijdstip 0 wordt de stroom gestart. Het netwerk bestaat uit een ethernetverbinding
en kan dus tot 1 MB/s aan. Net zoals bij TCP wordt begonnen met een ‘slow start’. We
weten op voorhand immers niet welk debiet het netwerk aankan. Indien de round trip time
(RTT), in de onderste grafiek van figuur 7.3, beperkt blijft zal de gebruikte bandbreedte
blijven toenemen. Na 6 seconden stijgt deze niet meer. Op dit punt was het ideaal van
20 beelden per seconde bereikt. Een hogere waarde zou de kwaliteit van het beeld immers
58
niet merkbaar verbeteren en dus een onnodige belasting van het netwerk vormen. Na ongeveer 20 seconden wordt de bandbreedte van het netwerk artificieel beperkt tot 2 kB/s.
Het ontvangen debiet blijft nog even constant doordat er een hoeveelheid data gebufferd
was. Daarna daalt het debiet sterk. Tegelijk begint de round trip time te stijgen doordat
een stijgend aantal pakketten zich in een netwerkbuffer bevinden en dus een vertraging
oplopen. Ze kunnen immers niet verstuurd worden daar het zenddebiet groter is dan het
beschikbare debiet over het netwerk. Het algoritme zal deze verhoogde RTT detecteren en
het zenddebiet terugschroeven. Op deze manier ontstaat een evenwicht waarbij het zenddebiet oscilleert rond een waarde iets onder het beschikbare debiet, en de RTT beperkt
blijft. Op 50 seconden wordt de beperking van het netwerk terug opgeheven. De pakketbuffers zullen zich kunnen ledigen zodat de RTT daalt. Hierdoor wordt de zendsnelheid
terug opgetrokken. Na 6 seconden is het maximum zenddebiet opnieuw bereikt.
Incrementele transfers
Bij de vorige bespreking werd er vanuit gegaan dat telkens een volledig beeld verstuurd
diende te worden. Het is echter mogelijk dat de samples relatief traag opgemeten worden
– of het netwerk een grote capaciteit heeft – zodat elk beeld slechts een kleine hoeveelheid
nieuwe data bevat. In dit geval is het voordeliger om enkel deze nieuwe data te versturen.
Wel mag er in dit geval geen verlies van pakketten door het netwerk optreden, daar het
nu wel eens lang kan duren voordat dezelfde data opnieuw opgemeten en dus verstuurd
kan worden1 . Het debiet dat hiervoor nodig is, kan berekend worden als de thread die
samples van de FPGA uitleest, bijhoudt hoe snel dit gebeurt. Indien dit kleiner is dan
het huidige zenddebiet (en er geen pakketverlies is opgetreden) wordt de overschakeling
gemaakt. Het maakt nu niet uit hoeveel pakketten er per seconde verstuurd worden. Het
debiet is immers constant en gelijk aan dat van de FPGA2 . Het is best deze snelheid zo
groot mogelijk te kiezen om de latentie laag te houden. Er werd gekozen voor 20 frames
per seconde. Dit is eveneens de maximumsnelheid die gebruikt wordt bij normale transfer.
7.5.2
Bulk transfer over TCP
Wanneer we de data niet in real-time moeten zien, of wanneer we de huidige data willen
opslaan om bijvoorbeeld in een document te verwerken, is transfer over UDP te omslachtig. Bovendien kan er dan pakketverlies optreden, wat we in dit geval willen vermijden.
Daarom werd voorzien dat de laatste metingen van elk frequentiepunt ook over de controle
TCP verbinding verstuurd kunnen worden. Het client programma kan deze (binaire) data
converteren naar een ASCII formaat dat geı̈mporteerd kan worden in bijvoorbeeld MS
1
We zouden dit kunnen opvangen door hier wel in een retransmissie te voorzien. Dit werd niet
geı̈mplementeerd.
2
De overhead van pakketheaders wordt hierbij genegeerd.
59
Excel of GNU Plot zodat er een grafiek van gemaakt kan worden. Om dit te demonsteren
werd een speciaal client-programma gemaakt dat via de webserver opgeroepen kan worden
en toelaat deze data te downloaden in CSV of XML formaat. Een vollediger beschrijving
is te vinden in paragraaf 8.2.
60
Hoofdstuk 8
Het gebruikersprogramma
8.1
Een client in Java
Er werd niet voorzien om het gebruikersprogramma volledig uit te werken. In het kader
van dit afstudeerwerk werd echter wel een testprogramma gemaakt dat de correcte werking
van de rest van de VNA kon aantonen. Om een idee te krijgen van wat de mogelijkheden
van een afgewerkt toestel zouden zijn, wordt hier een aantal vereisten gegeven voor het
gebruikersprogramma.
Wanneer een gebruiker een meting wenst te doen met de VNA zal hij eerst via zijn
webbrowser een verbinding maken met de VNA. Op de VNA draait een webserver zodat
de gebruiker een startpagina te zien krijgt en de mogelijkheid geboden wordt om het
gebruikersprogramma te downloaden. Dit is uitgevoerd als een Java applet en zal dus op
de PC van de gebruiker kunnen draaien, ongeacht dit een Windows, Linux of Mac machine
is, en zonder voorafgaande installatieprocedure.
Dit programma, verder de client genoemd, zal beginnen met een TCP verbinding op
te zetten met poort 99 van de VNA. Dit is de poort waarop WebVNA luistert. Vervolgens
zal de gebruiker d.m.v. menukeuzes of virtuele knoppen de frequentiepunten kiezen, de
attenuator instellen enz. Dit wordt door de client vertaald in opdrachten uit tabel 7.1.
Bovendien zal de client een UDP START opdracht geven zodat gestart wordt met het
doorsturen van samples.
Deze samples bevatten de numerieke waarden van amplitude en fase. Het is aan de
client om hiervan een grafiek te maken. Dit kan, volgens de keuze van de gebruiker, in
cartesiaans of polair formaat, of een ander formaat zoals een Smith kaart. Doordat de
parameters voor reflectie en transmissie tegelijk doorgestuurd worden, is het eveneens
mogelijk de Standing Wave Ratio (SWR, de verhouding tussen reflectie en transmissie)
weer te geven.
Meestal wil de gebruiker de opgemeten resultaten exporteren, bijvoorbeeld om deze
verder te verwerken of op te nemen in een document. Hiertoe kan de client de samples
61
Figuur 8.1: Schermafbeelding van het testprogramma.
gebruiken die over UDP verstuurd werden, of er kan een kopie opgevraagd worden over de
TCP verbinding via de TCP DUMP opdracht. Deze gegevens kunnen dan in een gepast
formaat opgeslagen worden op de harde schijf van de gebruiker.
Doordat de client op een volwaardige PC draait, hoeft er geen beperking te zijn op
de complexiteit van de opdrachten die de gebruiker kan geven. Zo is het bijvoorbeeld
mogelijk om in een scripting taal te voorzien, eventueel gebaseerd op een bestaande taal
zoals Tcl/Tk, zodat metingen geautomatiseerd kunnen worden. Deze scripts kunnen in
tekstformaat ingegeven worden of via een klik-en-sleep benadering. Het invoeren van dergelijke scripts op een stand-alone eenheid, met beperkte display en aantal knoppen, is zeer
omslachtig, zodat in dit soort mogelijkheden meestal niet voorzien is.
In het meest extreme geval is er zelfs geen gebruiker en gebeuren alle metingen via
scripting: dit kan dan gebeuren door een client die op het VIA moederbord in de VNA
zelf draait. Op deze manier kan een automatische bewakingseenheid gebouwd worden
die metingen of alarmcondities over het Internet, bijvoorbeeld als e-mail of via instant
messenging, naar buiten stuurt. Indien voorzien wordt in een optische S-parameter testset
kan bijvoorbeeld de karakteristiek van een optische vezel opgemeten worden; deze kan
dan toelaten de uitzetting van een drukvat op te volgen. Diezelfde optische vezels worden
geı̈ntegreerd in betonnen structuren zoals bruggen zodat ook hiervan de belasting gevolgd
kan worden. Een overbelasting ten gevolge van een overladen vrachtwagen wordt meteen
gesignaleerd. Via scripting kan men ervoor zorgen dat een op de VIA aangesloten webcam
62
Figuur 8.2: Weergave van voedingsspanning en temperatuur van de CPU via de webserver.
een foto van de overtreder neemt, waarna deze in een e-mailbericht naar de bevoegde
instanties kan worden doorgestuurd.
8.2
Andere mogelijkheiden van de webserver
Het gebruik van een webserver biedt, naast het laten downloaden van de gebruikerssoftware, nog andere toepassingen. Zo is het mogelijk om via de webserver een programma te
starten en de resultaten ervan als een HTML pagina of in een ander formaat terug te krijgen. Dit is het principe dat gebruikt wordt bij interactieve webpagina’s zoals bijvoorbeeld
zoekrobotten.
Om deze mogelijkheid te demonsteren werd een programma geschreven dat verbinding
maakt met WebVNA en via de TCP DUMP opdracht de huidige meetresultaten opvraagt.
Deze worden omgezet in een CSV of XML formaat en doorgegeven aan de gebruiker.
Op dezelfde manier kunnen, via een formulier op een HTML pagina, gegevens verstuurd
worden naar een programma. Zo zou men de frequentiepunten kunnen instellen zodat de
VNA compleet via een webbrowser, zonder de Java client, bestuurd kan worden.
Een andere toepassing is het weergeven van status informatie. Zo bevatten de meeste
moderne moederborden een IC dat de voedingsspanning en de temperatuur van de CPU
in de gaten houdt. Deze gegevens kunnen eveneens opgevraagd worden via de webserver.
63
Hoofdstuk 9
De toekomst
9.1
IPv6
In de beschrijving van de voor de SHARC ontwikkelde TCP/IP stack [MR02] werd een
hoofdstuk gewijd aan het implementeren van IPv6. De bestaande code kan hiertoe aangepast worden. Dit is echter een niet triviale opgave. Wanneer we de VNA baseren op Linux
krijgen we IPv6 cadeau: sinds kernel versie 2.1.8 (rond 1996) is ondersteuning voor IPv6
aanwezig in de kernel. We hoeven dit protocol dus niet zelf te implementeren. Er is slechts
één aanpassing nodig in de zelfgeschreven code: dit is in de UDP START opdracht van
WebVNA. Deze verwacht immers een IP adres waarnaar de samples gestuurd dienen te
worden. Dit veld zal uitgebreid moeten worden tot 128 bit om het langere IPv6 adres te
kunnen bevatten.
9.2
Beveiliging
Wanneer de VNA met het publieke Internet verbonden wordt, kunnen potentieel honderden miljoenen gebruikers ermee verbinding maken. Dat niet iedereen hiervan goede
intenties heeft, is reeds meerdere malen aangetoond. Het is dus nodig te voorzien in een
vorm van beveiliging zodat enkel geautoriseerde personen de VNA kunnen besturen.
De webserver is eenvoudig te beveiligen door het HTTPS protocol te gebruiken. Dit is
een aanpassing van het gewone HTTP protocol, dat loopt over een geëncrypteerde TCP
verbinding. Hiertoe wordt SSL gebruikt. Dit is een bibliotheek die gebruikt kan worden
als laag tussen TCP en de applicatielaag, in dit geval HTTP. Hiermee is het verkeer dat
over de verbinding loopt onleesbaar voor buitenstaanders en is het mogelijk een gebruiker
te verplichten zich te authentiseren alvorens enige communicatie mogelijk is.
Dezelfde weg kunnen we volgen voor de controleconnectie tussen het gebruikersprogramma en WebVNA: door beiden van de SSL bibliotheek gebruik te laten maken zullen
enkel gebruikers in het bezit van de correcte sleutel de VNA kunnen besturen. Verder
64
zal het nodig zijn dat WebVNA slechts opdrachten toelaat van één client. Wanneer twee
gebruikers op hetzelfde moment een ander stel frequentiepunten willen instellen, krijgen
we uiteraard problemen. De tweede gebruiker kan echter wel een UDP stroom opzetten
of een TCP dump aanvragen, zo is het mogelijk dat een gebruiker het client-programma
draait en tegelijk een CSV bestand met de metingen download van de webserver. Het is
dus te beperkend om het aantal controleconnecties tot één te beperken.
9.3
Kalibratie
Vanuit het oogpunt van de besturingssoftware, werd voorlopig niet voorzien in een mogelijkheid tot kalibratie van de VNA. Dit is echter wel wenselijk daar vooral de analoge
componenten een bepaalde frequentiekarakteristiek hebben die wel opgemeten wordt, maar
niet tot die van het te meten netwerk behoort. Indien deze karakteristiek gekend is – deze
is op te meten door de geijkte metingen door te voeren – kunnen de meetresultaten aangepast worden zodat deze component teniet gedaan wordt. Het is mogelijk deze aanpassing
door de client te laten doen. Deze karakteristiek is echter een eigenschap van de VNA zelf.
Het is dus logischer deze stap door WebVNA te laten uitvoeren.
9.4
Eliminatie van de harde schijf
Een volledige Linux distributie neemt al snel tot 1 GiB schijfruimte in beslag. Hierin
zijn echter een groot aantal pakketten opgenomen die door de VNA nooit gebruikt zullen
worden, zoals het X Windows systeem of de GCC compiler. Door deze pakketten niet te
installeren is het mogelijk de gebruikte schijfruimte sterk terug te dringen. Bovendien zijn
voor sommige pakketten alternatieven die een stuk kleiner zijn. De standaard webserver
in een desktop Linux distributie bijvoorbeeld is Apache en neemt enkele tientallen MiB
in beslag. Deze heeft echter veel meer mogelijkheden dan voor deze toepassing nodig zijn.
We kunnen dan ook kiezen voor een kleinere webserver, bv. thttpd, die slechts 65 KiB
schijfruimte nodig heeft. Op deze manier zijn distributies te maken die op één floppy van
1.4 MiB passen. Het ligt dus binnen de mogelijkheden om de harde schrijf te vervangen door
een Flash module welke zich voordoet als een harde schijf en daarom geen enkele aanpassing
in de software nodig heeft, maar sneller, zuiniger, kleiner en meer schokbestendig is. De
opslagcapaciteit van zo’n module is typisch enkele tientallen MiB. Dit is voldoende voor
de hier beschreven toepassing.
65
Hoofdstuk 10
Conclusie
10.1
DSP platform
Hardwarematig gezien is het DSP platform zeer interessant. Er is slechts een klein aantal
componenten nodig (DSP, geheugen, ethernetcontroller) die alle door het labo zelf op een
PCB geplaatst kunnen worden en dus in een goedkope, compacte en energiezuinige controller resulteren. Vanuit het perspectief van de software zijn er echter een aantal nadelen.
Het was nodig zelf een TCP/IP stack te ontwikkelen, wat geen triviale opgave is. Ook de
webserver en het bestandssysteem dienden zelf gemaakt te worden, daar voor de SHARC
dergelijke software niet voorhanden is. Bovendien is een DSP architecturaal gezien niet
geoptimaliseerd voor het draaien van netwerkcode. Zo werken de meeste netwerkprotocollen met ASCII tekst (in 8 bit woorden dus), terwijl de DSP enkel met 32 bit woorden
kan werken. Men heeft dan de keuze om toch met het C datatype char te werken – dit
gebruikt de natuurlijke woordlengte van 32 bit en verspilt dus 75% van het geheugen – of
om zelf bij elke manipulatie van netwerkdata de juiste byte te selecteren. Dit maakt het
programmeren van bv. een HTTP-server vrij moeizaam. Hierdoor zal bij keuze voor dit
platform de kostprijs vooral bepaald worden door de software. Dit is een vaste kost, dus
voor grote oplagen kan dit platform toch een goede keuze zijn.
10.2
Linux en USB
Door het gebruik van een standaard Linux kernel heeft dit platform een zeer solide basis.
De netwerkcode is reeds grondig getest en wordt voortdurend bijgewerkt; webservers e.d.
zijn vrij beschikbaar. Hierdoor is het volume zelf te schrijven code sterk teruggebracht.
De complexiteit hiervan is relatief laag. Er is wel een grondige kennis van de interne
werking van de Linux kernel voor nodig. De systeemvereisten voor Linux zijn echter niet
de minste. Zelfs de kleinste distributies rekenen nog steeds op een moderne 32 bit processor
met enkele tientallen megabytes RAM en minstens evenveel opslagruimte. Zelf een PCB
66
ontwerpen met deze componenten is niet eenvoudig. Wanneer we onze toevlucht nemen tot
een standaard moederbord heeft dit een gegeven formaat en bepaalde externe interfaces.
Inbouw in een klein en zuinig apparaat wordt dus bemoeilijkt. Bovendien is de kostprijs
van deze hardware relatief hoog. Deze wordt echter gecompenseerd door een lage prijs van
de software, zodat dit platform voordeel zal bieden bij een kleine oplage.
Hardware
Complexiteit
Verbruik
Flexibiliteit
Kostprijs
Software
Complexiteit
Systeemvereisten
Kostprijs
Totaal
Grootste kost
Best bij oplage
DSP
Linux + USB
Gemiddeld
Laag
Goed
Laag
Laag
Hoog
Beperkt
Hoog
Hoog
Laag
Hoog
Gemiddeld
Hoog
Laag
Software (vast)
Groot
Hardware (variabel)
Klein
Tabel 10.1: Vergelijking tussen beide platformen.
10.3
Besluit
In dit afstudeerwerk werden twee mogelijke platformen bekeken die de controle van een
over Internet bestuurbare Vector Netwerk Analyser op zich kunnen nemen. Voor het eerste
platform werden enkele subsystemen uitgewerkt, wat toeliet de kwaliteiten en problemen
van dit platform in te schatten. Op het tweede platform werd een meer volledige implementatie gedaan van alle nodige componenten. In het bijzonder werd aandacht besteed
aan het optimaliseren van de doorvoersnelheid en de betrouwbaarheid van de USB verbinding; en het implementeren van een algoritme voor flow-control, zodat het toestel met
verschillende types netwerken kan samenwerken.
67
Bijlage A
Interface van LibVNA
vna open()
int vna open(const char * file)
Opent het device met de naam file (dit is meestal /dev/fx2a) en retourneert de file descriptor of een negatieve foutcode indien het device bijvoorbeeld niet aangesloten is.
vna close()
int vna close(int fd)
Sluit het device dat beschreven wordt door de file descriptor fd.
vna bulk read()
int vna bulk read(int fd, int ep, void * data, int size, int timeout)
Gebruikt de ioctl() functie met de USB FX2 BULK opdracht om size bytes uit het endpoint ep te lezen. Indien het device niet reageert na timeout jiffies (dit is 1/100e seconde)
wordt een foutcode teruggegeven. fd is de file descriptor van een reeds geopend device.
vna bulk write()
int vna bulk write(int fd, int ep, void * data, int size, int timeout)
Gebruikt de ioctl() functie met de USB FX2 BULK opdracht om data naar endpoint
ep te versturen.
68
vna command()
int vna command(int fd, void * buffer out, int size out,
void * buffer in, int size in, int timeout)
Combineert een oproep van vna bulk write() en vna bulk read() met respectievelijke
endpoints EP1OUT en EP1IN. Hiermee kan men een opdracht aan de FX2 geven en het
resultaat teruglezen.
vna set read()
int vna set read(int fd, int endpoint)
Selecteert het endpoint dat gebruikt zal worden bij read() calls. Dit gebeurt door gebruik
te maken van de ioctl() functie met de USB FX2 SET EP opdracht.
vna set write()
int vna set write(int fd, int endpoint)
Selecteert het endpoint dat gebruikt zal worden bij write() calls.
vna get read()
int vna get read(int fd)
Retourneert het endpoint dat gebruikt wordt bij read() calls. Dit gebeurt door gebruik
te maken van de ioctl() functie met de USB FX2 GET EP opdracht.
vna get write()
int vna get write(int fd)
Retourneert het endpoint dat gebruikt wordt bij write() calls.
vna set ep6 len()
int vna set ep6 len(int fd, int len, int timeout)
Selecteert het aantal bytes dat in een FIFO van EP6IN moet zitten vooraleer deze naar
het USB domein overgedragen wordt. Deze functie maakt gebruik van vna command() en
de firmware opdracht SET EP6 LEN.
69
vna i2c read()
int vna i2c read(int fd, int address, void * data, int size, int timeout)
Leest size bytes uit I2 C adres address en plaatst deze in data.
vna i2c write()
int vna i2c write(int fd, int address, void * data, int size, int timeout)
Schrijft de size bytes in de buffer data naar I2 C adres address.
vna com send()
int vna com send(int fd, unsigned char data, int timeout)
Verstuurt de byte data met USART1 van de FX2 naar de Z8 in de S-parameter testset.
vna fpga program()
int vna fpga program(int fd vna, int fd bitfile)
Programmeert de FPGA. Hiertoe wordt eerst een FPGA PROGRAM opdracht gegeven.
Daarna wordt het programmeerbestand via EP2OUT naar de FX2 geschreven. Dit
moet reeds geopend zijn en wordt doorgegeven via het argument fd bitfile. Er wordt
verondersteld dat in dit bestand alle bytes reeds gespiegeld werden, zodat het met de
minst significante bits eerst verstuurd kan worden.
Tenslotte wordt de opdracht FPGA PROGRAM END gegeven. Het resultaat hiervan
(het niveau van de IN IT en DON E pinnen) wordt teruggegeven.
70
Bibliografie
[RFC3448] Handley, et. al. TCP Friendly Rate Control (TFRC): Protocol Specification.
Request for Comments: 3448, http://www.ietf.org/rfc/rfc3448.txt
[Lam00]
Peter Lambrecht. Een Vector Netwerk Analyser (VNA) als studieobject voor
geavanceerd RF ontwerp. Doctoraatsthesis, Universiteit Gent, Faculteit Toegepaste Wetenschappen, Vakgroep Informatietechnologie (INTEC), onderzoeksgroep INTEC design, April 2000.
[LP03]
Pascal Lotiquet en Wim Pletinckx. Ontwerp van de firmware van een VNA.
Afstudeerwerk, Universiteit Gent, Faculteit Toegepaste Wetenschappen, Vakgroep Informatietechnologie (INTEC), onderzoeksgroep INTEC design, 2003.
[LH03]
Carl Lylon en Vik Van Hoye. Ontwerp van een VNA S-parameter testset.
Afstudeerwerk, Universiteit Gent, Faculteit Toegepaste Wetenschappen, Vakgroep Informatietechnologie (INTEC), onderzoeksgroep INTEC design, 2003.
[MR02]
Bart Meerschman and Koen Van Renterghem. Ontwerp van de netwerksoftware van een breedband, web bestuurde oscilloscoop. Afstudeerwerk, Universiteit
Gent, Faculteit Toegepaste Wetenschappen, Vakgroep Informatietechnologie
(INTEC), onderzoeksgroep INTEC design, 2002.
[Pau03]
Karel Pauwels. Ontwerp van een VNA ontvanger. Afstudeerwerk, Universiteit
Gent, Faculteit Toegepaste Wetenschappen, Vakgroep Informatietechnologie
(INTEC), onderzoeksgroep INTEC design, 2003.
[RC01]
Alessandro Rubini and Jonathan Corbet. Linux Device Drivers, 2nd Edition.
O’Reilly, June 2001.
[AT01]
Andrew S. Tanenbaum. Modern operating systems, 2nd Edition. Prentice Hall,
2001.
[USB2.0]
Universal Serial Bus Revision 2.0 specification.
http://www.usb.org/developers/docs/usb 20.zip
71
Download