Bygg och programmera FPGA-baserade konstruktioner snabbt med Python och Jupyter-notebooks

Av Stephen Evanczuk

Bidraget med av DigiKeys nordamerikanska redaktörer

Konstruktörerna har länge förlitat sig på FPGA-kretsar (field programmable gate arrays) för att accelerera prestandan i maskinvaror för krävande tillämpningar som datorsyn, kommunikation, industriella inbäddade system och, i allt högre grad, Internet of Things (IoT). Men de många momenten inom konventionell FPGA-programmering har varit ett hinder som fått konstruktörerna att försöka hitta alternativa lösningar – fram tills nu.

Framväxten av utvecklingsplattormen Python Productivity for Zynq (PYNQ), som baseras på Jupyter-notebook, tar sig an problemet med FPGA-programmerbarhet. Med ett utvecklingskort som stöder PYNQ kan utvecklare med mindre FPGA-erfarenhet snabbt implementera funktioner och dra full nytta av FPGA:s prestanda för beräkningsintensiva tillämpningar.

I den här artikeln börjar vi med att beskriva den typiska FPGA-metoden och sedan visar vi hur du kommer igång med att använda ett utvecklingskort från Digilent, som ger ett kraftfullt öppenkodsbaserat alternativ för att accelerera utvecklingen av FPGA-baserade system.

Varför FPGA-kretsar?

Ingenjörer som behöver använda komplexa, beräkningsintensiva algoritmer förlitar sig ofta på FPGA-kretsar för att kunna accelerera projektet utan att äventyra budgeten. FPGA-kretsar har i själva verket blivit den dominerande plattformen för att öka prestandan för artificiell intelligens inom edge-computing-system (se ”Använda FPGA:er för att bygga högpresterande visionstillämpningar med maskininlärning”).

Specifikt för inbäddade tillämpningar används mer avancerade FPGA-SoC-enheter (system-on-chip) med integrerad programmerbar logik och mikrostyrenhet. Ett exempel är Xilinx SoC-enhet Zynq-7000, som kombinerar en dubbelkärnig Arm® Cortex®-A9-processor med upp till 444 000 logikceller i den programmerbara logiken (figur 1). Med de inbyggda processorerna och en bred uppsättning av extrakomponenter erbjuder Zynqs SoC upp till 2 020 DSP-block (digital signal processor). Med hjälp av de här resurserna kan utvecklarna konfigurera programmeringslogiken till de specialiserade signalbehandlingskedjor som krävs för att accelerera flödet genom krävande algoritmer.

Diagram av Xilinx Zynq-7000 SoC

Figur 1: Xilinx Zynq-7000 SoC kombinerar en dubbelkärnig Arm Cortex-A9-processor, programmerbar logik och en omfattande uppsättning av extrakomponenter och gränssnitt som behövs i många inbäddade tillämpningar. (Bildkälla: Xilinx)

Integrering av processorer och programmerbar logik gör att operationer kan utföras i bussar på chippet istället för i komponenter utanför chippet. Tack vare integreringen förenklas inläsning av den programmerbara logiken vid start eller i återställningssekvenser – en kritisk uppgift.

I ett typisk mikrostyrenhetsbaserat system med en FPGA, var utvecklarna tvungna att hantera sekvenser och säkerhet för inläsning av de bitströmmar som programmerar FPGA-enheten. Med Zynq-SoC:en är det en integrerad processor som sköter de uppgifter som annars utförs av en konventionell mikrostyrenhet, till exempel hantering av den programmerbara logiken och andra komponenter på chippet. Det gör att FPGA-kretsens inläsningsprocess mer liknar en konventionell mikrostyrenhets bootprocess än vanlig FPGA-bitströmsinitiering.

Bootprocessen inträffar under en kort sekvens av steg som hanteras av en av Zynq-processorerna (figur 2). Vid uppstart eller återställning, inleds bootprocessen när en Zynq-processor kör en kort kodsekvens från sitt skrivskyddade BootROM, för att hämta den egentliga bootkoden från en bootenhet. Utöver kod för att konfigurera processorsystemkomponenterna, innehåller bootkoden PL-bitströmmen och användartillämpningen. När inläsning av bootkoden är klar, använder processorn den inkluderade bitströmmen för att konfigurera programmeringslogiken (PL). När konfigurering och PL-konfigurering har slutförts börjar enheten köra den tillämpning som ingår i bootkoden.

Bild av Xilinx Zynq-7000 SoC bootsekvens

Figur 2: I en bootsekvens som liknar bootsekvensen hos mikrostyrenheter, startar Xilinx Zynq-7000 SoC-enheten kodkörning från ett boot-ROM som läser in och kör bootinläsaren. Denna hanterar de efterföljande stegen, till exempel att använda en bitström inuti bootkoden, för att konfigurera den programmerbara logiken. (Bildkälla: Xilinx)

Även med förenklad hantering av PL-inläsning har utvecklarna tidigare varit tvungna att hantera den komplexa FPGA-utvecklingsprocessen som krävs för att generera nödvändiga bitströmmar. För utvecklare som vill kunna dra fördel av FPGA-kapaciteten har den konventionella FGPA-utvecklingsprocessen länge utgjort ett väsentligt hinder för implementering. Med PYNQ-miljön eliminerade Xilinx det hindret.

PYNQ-miljön

I PYNQ är PL-bitströmmarna inkapslade i förbyggda bibliotek, s.k. overlays (”överlägg” hädanefter i denna text), som har en liknande roll som programbibliotek i utvecklingsprocessen och körningsmiljön. Under bootinläsningen konfigureras den programmerbara logiken av bitströmmar som är associerade med överläggen. Den processen är transparent för utvecklare som drar fördel av överläggets funktioner via det Python-API (application programming interface) som är associerat med varje överlägg. Utvecklaren kan kombinera programbibliotek och överlägg efter behov, och använda de olika API:erna för att implementera tillämpningen. Vid körning startas programbibliotekkoden av processorsystemet som vanligt, medan programmeringslogiken implementerar de funktioner som ingår i överlägget. Resultatet är den typ av accelererade prestanda som fortsätter att hålla liv i intresset för FPGA-baserade konstruktioner för krävande tillämpningar.

Som framgår av namnet drar PYNQ fördel av produktivitetsfördelar som fås av programmeringsspråket Python. Python har seglat upp som ett av de främsta språken, inte enbart för att det är relativt enkelt, utan också tack vare det stora och ständigt växande ekosystemet. I kataloger som innehåller öppna Python-moduler har utvecklarna stor chans att hitta de programbibliotek som behövs för stödtjänster eller specialiserade algoritmer. Samtidigt kan utvecklarna implementera kritiska funktioner i språket C, eftersom PYNQ använder C-implementationen av Python-tolken. Implementeringen ger enkel åtkomst till tusentals befintliga C-bibliotek, och förenklar användandet av C-språksbibliotek som har genererats av utvecklare. Erfarna utvecklare kan utöka PYNQ med specialiserade maskinöverlägg och C-bibliotek, men PYNQ:s verkliga styrka är förmågan att erbjuda en högeffektiv utvecklingsmiljö för alla som kan bygga Python-program.

PYNQ, i sig ett öppenkodsprojekt, baseras på en annat sådant projekt, nämligen Jupyter notebook. Jupyter-notebooks erbjuder en särskilt effektiv miljö för interaktivt utforskande av algoritmer och prototyping av komplexa tillämpningar i Python eller något av de andra programmeringsspråken som stöds, för närvarande över 40 stycken. I en Jupyter-notebook, som utvecklas i samförstånd av communityt kring Project Jupyter, kombineras körbara kodrader med beskrivande text och grafik. Det gör det möjligt för enskilda utvecklare att på ett effektivare sätt dokumentera sitt arbete utan att övergå till en annan utvecklingsmiljö. Till exempel kan en utvecklare använda en notebook där några kodrader, som behövs för att visa informationen, kombineras med den grafik som genereras av koden (figur 3).

Bild av Jupyter-notebook från en Xilinx-exempelkatalog

Figur 3: I en Jupyter-notebook från en Xilinx-exempelkatalog kombineras beskrivande text, körbar kod och ett utresultat associerat med en tillämpning. (Bildkälla: Xilinx)

En Jupyter-notebook kan innehålla kod, utresultat och beskrivande text, eftersom det är ett levande dokument som administreras i en interaktiv utvecklingsmiljö levererad av Jupyter notebook-servern (figur 4). Under en Jupyter-session återges notebook-filen (av servern) i en vanlig webbläsare med hjälp av HTTP, och en kombination av HTTP och Websocket-protokoll för det statiska och dynamiska innehållet i det återgivna dokumentet. På backend-sidan kommunicerar servern med en kodexekveringskärna, med hjälp av öppenkodsprotokollet ZeroMQ (ØMQ).

Diagram över arbetsflöde för Jupyter-sessioner

Figur 4: Notebook-servern i en Jupyter-session renderar notebook-filens innehåll till en webbläsare och interagerar med en backend-kärna som kör koden. (Bildkälla: Project Jupyter)

I redigeringsläget kan användaren modifiera text och kod. Servern uppdaterar motsvarande notebook-fil, vilket är en textfil som innehåller en uppsättning av JSON-nyckel/värdepar. I Jupyter-miljön kallas sådana par för celler. Exempel på sådana celler, med kod och markdown-text, ses i webbläsarbilden av en Jupyter-notebook ovan (listing 1).

Copy { "cell_type": "markdown", "metadata": {}, "source": [ "## Error plot with Matplotlib\n", "This example shows plots in notebook (rather than in separate window)."
] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAA[truncated]", "text/plain": [ "<matplotlib.figure.Figure at 0x2f85ef50>" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", " \n", "X = np.arange(len(values))\n", "plt.bar(X + 0.0, values, facecolor='blue', \n", " edgecolor='white', width=0.5, label=\"Written_to_DAC\")\n", "plt.bar(X + 0.25, samples, facecolor='red', \n", " edgecolor='white', width=0.5, label=\"Read_from_ADC\")\n", "\n", "plt.title('DAC-ADC Linearity')\n", "plt.xlabel('Sample_number')\n", "plt.ylabel('Volts')\n", "plt.legend(loc='upper left', frameon=False)\n", "\n", "plt.show()" ] }, 

Listing 1: En Jupyter-notebook är en textfil som innehåller en uppsättning av JSON-nyckel/värdepar med kodsektioner, markup och utresultat, som i det här exemplet motsvarar den sida som återges i figur 3. Observera att den sträng som motsvarar png-bilden i figur 3, här har förkortats av visningsskäl. (Kodkälla: Xilinx)

Utöver dokumentationsmöjligheterna ligger en del av Jupyter-miljöns styrka i förmågan att exekvera kodceller interaktivt. Utvecklarna väljer önskad cell i webbläsaren (med blå kant i figur 3) och klickar sedan på Run-knappen i Jupyter-menyn högst upp i webbläsarfönstret. Jupyter-notebook-servern lämnar sedan över motsvarande kodcell till en kodexekveringskärna, som är den interaktiva Python-kärnan (IPython) i PYNQ-miljön. Efter kodexekveringen uppdaterar servern, asynkront, både den återgivna webbsidan och notebook-filen med det utresultat som genereras av kärnan.

PYNQ utökar den här metoden till FPGA-baserad utveckling, genom att bädda in Jupyter-ramverket, inklusive IPython-kärnan och notebook-webbservern på Zynq SoC:s Arm-processorer. Den PYNQ Python-modul som ingår i miljön ger tillgång till det Python-API som behövs för att komma åt PYNQ-tjänsterna i Python-program.

FPGA-utvecklingsmiljön

Digilents utvecklingssats PYNQ-Z1, utformad specifikt för att stödja PYNQ, gör det möjligt för utvecklarna att snabbt börja undersöka FPGA-accelererade tillämpningar genom att läsa in PYNQ:s körbara Linux-avbild. PYNQ-Z1-kortet kombinerar en Xilinx XC7Z020 Zynq-SoC med 512 Mbyte RAM, 16 Mbyte flash och en microSD-plats för ytterligare flashminne externt. Kortet – med omkopplare, knappar, LED:ar och flera ingångar/utgångar – har också anslutningar för expansion till tredjepartsmaskinvara, via Digilent Pmod-gränssnittet (peripheral module) och Arduinos expansionskort och Digilent chipKIT-kort. Kortet ger också tillgång till Zynq SoC:s AD-omvandlare, kallad XADC, som sex enpoliga analoga indataportar eller fyra differentiella analoga indataportar. Digilent erbjuder också den fristående produktivitetssatsen PYNQ-Z1, som innehåller strömförsörjning, en micro-USB-kabel, ett microSD-kort med en förinläst PYNQ-avbild samt en Ethernet-kabel för att uppdatera eller lägga till Python-moduler.

För utvecklaren är SoC-enhetens och kortets fulla kapacitet tillgänglig direkt via en Jupyter-notebook. För att till exempel komma åt kortets Pmod-gränssnitt för läsning av AD-omvandlarens och skrivning av DA-omvandlarens värden i ett loopback-test, krävs endast ett fåtal kodrader (figur 5). När nödvändiga Python-moduler har importerats, initieras SoC-programmeringslogiken med ett ”basöverlägg” (cell två i figur 5). Som ett konventionellt kortstödpaket ger det här basöverlägget tillgång till kortets extrakomponenter.

Bild av Jupyter-notebook från Xilinx-exempelkatalogen

Figur 5: En Jupyter-notebook från Xilinx-exempelkatalogen visar det enkla designmönstret associerat med åtkomst av maskinvarutjänster för input-/outputtransaktioner. (Bildkälla: Xilinx)

Utvecklaren behöver bara anropa de importerade modulerna för att läsa och skriva värdena (cell tre i figuren). I den exempel-notebook som visas, återger notebook-servern varje cell i tur och ordning, och uppdaterar notebooken med de genererade resultaten. I det här fallet är 0,3418 det enda utvärdet, men eventuella körningsfel visas som vanliga Python-traceback-stackar för respektive kodcell.

Bygga komplexa tillämpningar

Bakom denna skenbart enkla metod för att utveckla inbäddade tillämpningar, kompletterad av den breda uppsättningen Python-moduler, döljer sig en kraftfull plattform för snabb implementering av komplexa, beräkningsintensiva tillämpningar. Till exempel kan utvecklaren snabbt implementera en ansiktsigenkännande webbkamera med hjälp av PYNQ-Z1 HDMI-indata och det populära OpenCV-biblioteket för datorsyn. Efter inläsning av basöverlägget och webbkameragränssnittet, initierar utvecklaren ett OpenCV-kameraobjekt videoIn (figur 6). Avläsning av videobilden är sedan så enkelt som att anropa videoIn.read(), vilket returnerar frame_vga i detta exempel.

Bild av Jupyter-notebook från Xilinx-exempelkatalogen

Figur 6: En Jupyter-notebook i Xilinx-exempelkatalogen visar hur utvecklarna snabbt kan bygga ett ansiktsigenkänningssystem för webbkameror genom att kombinera PYNQ-Z1-utvecklingskortets maskinresurser med de kraftfulla bildbearbetningsfunktionerna i OpenCV-biblioteket (cv2). (Bildkälla: Xilinx)

I ett efterföljande steg, som hanteras som en separat cell i notebooken, skapar utvecklaren OpenCV-klassificeringsobjekt (cv2) med hjälp av förinställda kriterier, och lägger till avgränsningsrutor för att identifiera egenskaper (grönt för ögon och blått för ansikten, i det här exemplet). I ett par andra celler avslutas tillämpningen med visning av utresultatet via kortets HDMI-utgång (figur 7).

Bild av de sista cellerna i Xilinx-notebook för ansiktsigenkänning för webbkamera

Figur 7: I de sista cellerna i denna Xilinx-notebook för ansiktsigenkänning i webbkameror, illustreras användningen av OpenCV-klassificerare. Klassificerarnas resultat används för att lägga till avgränsningsrutor i de ursprungliga bilderna och för att visa resultatet via PYNQ-Z1-utvecklingskortets HDMI-utgång. (Bildkälla: Xilinx)

Möjligheten att interaktivt bygga, testa och föra diskussioner kring komplex programvara har gjort Jupyter-notebooks till en favorit bland forskare och ingenjörer som arbetar med att optimera algoritmer för artificiell intelligens. Vartefter arbetet fortskrider, tillförs notebooken – förutom kod och utresultat – utvecklarnas analyser kring resultaten, vilket ger en sorts datormässig historik som kan delas med kollegor.

Utvecklarna bör dock ha förståelse av att notebooks inte är särskilt lämpliga som kataloger för mer produktionsorienterade uppgifter. Om man till exempel inkluderar stora hexadecimala kodsträngar för bilddata (se förkortad sektion i Listing 1) ökar dokumentstorleken, och olika metoder som används av typiska källversionskontrollsystem kan bli mycket komplicerade. Blandningen av kod och icke-funktionell text kan göra det komplicerat att migrera kod som har skapats i tidiga analysskeden till utvecklingsprocesser på produktionsnivå. Men för experimenterande och snabb prototyping erbjuder Jupyter-notebooks en kraftfull utvecklingsmiljö.

Slutsatser

FPGA-kretsar erbjuder den prestandaökning som behövs för att kunna möta de krav som ställs på inbäddade system för IoT, datorsyn, industriell automation, bilindustrin och mycket annat. Konventionella FPGA-utvecklingsmetoder har länge varit ett hinder för många utvecklare, men den Python-baserade PYNQ-utvecklingsmiljön, baserad på Jupyter-notebooks, erbjuder ett effektivt alternativ. Med ett utvecklingskort som stöder PYNQ kan utvecklare med mindre FPGA-erfarenhet snabbt implementera funktioner och dra full nytta av FPGA:s prestanda för beräkningsintensiva tillämpningar.

DigiKey logo

Disclaimer: The opinions, beliefs, and viewpoints expressed by the various authors and/or forum participants on this website do not necessarily reflect the opinions, beliefs, and viewpoints of DigiKey or official policies of DigiKey.

Om skribenten

Image of Stephen Evanczuk

Stephen Evanczuk

Stephen Evanczuk har arbetat i mer än 20 år med att skriva för och om elektronikbranschen inom många olika områden som maskinvara, programvara, system och applikationer – inklusive sakernas internet. Han har en kandidatexameni neurovetenskap om artificiella neuronnät och har arbetat inom rymdfartsindustrin med mycket distribuerade säkra system och metoder för acceleration av algoritmer. När han inte skriver artiklar om teknik och konstruktion arbetar han med applikationer för djupinlärning för igenkänningssystem och rekommendationssystem.

Om utgivaren

DigiKeys nordamerikanska redaktörer