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