Snel op FPGA gebaseerde ontwerpen bouwen en programmeren met Python en Jupyter Notebooks

Door Stephen Evanczuk

Bijgedragen door De Noord-Amerikaanse redacteurs van DigiKey

Ontwerpers maken al tijden gebruik van field-programmable gate arrays (FPGA's) om de prestaties van hardware-ontwerpen voor reken-intensieve toepassingen zoals computer vision, communicatie, industriële ingebedde systemen en het Internet of Things (IoT) te versnellen. De gedetailleerde stappen, die bij conventionele FPGA-programmering komen kijken, vormen echter een groot struikelblok, waardoor ontwerpers tot nu toe vaak op zoek gingen naar alternatieve verwerkingsoplossingen.

De introductie van de op Jupyter notebooks gebaseerde 'Python Productivity for Zynq' (PYNQ)-ontwikkelingsomgeving biedt een oplossing voor het probleem van het programmeren van FPGA's. Door een ontwikkelingsbord te gebruiken dat specifiek is ontworpen om PYNQ te ondersteunen, kunnen ook ontwikkelaars met weinig ervaring op het gebied van FPGA's snel ontwerpen implementeren die ten volle van de prestaties van FPGA's profiteren, om reken-intensieve toepassingen sneller te maken.

Dit artikel beschrijft de typische FPGA benadering en laat vervolgens zie hoe men aan de slag gaat met een ontwikkelingsbord van Digilent, dat een krachtig open-source alternatief biedt om de ontwikkeling van op FPGA gebaseerde systemen te versnellen.

Waarom FPGA's?

Ingenieurs die complexe, reken-intensieve algoritmen moeten gebruiken, kiezen vaak voor FPGA's om de uitvoering te versnellen zonder dat strak bemeten 'energiebudgetten' hierdoor in gevaar komen. FPGA's zijn dan ook een dominant platform gebleken voor het versnellen van algoritmen voor kunstmatige intelligentie in edge-computing-systemen (zie “FPGA's gebruiken om krachtige ingebedde vision-toepassingen te bouwen met machine-learning”).

Meer geavanceerde FPGA system-on-chip (SoC)-apparaten, die specifiek zijn ontworpen voor ingebedde toepassingen, integreren programmeerbare logische (PL) structuur met een microcontroller. De Xilinx Zynq-7000 SoC combineert bijvoorbeeld een dual-core Arm® Cortex®-A9 processorsysteem met maximaal 444.000 logische cellen in zijn geïntegreerde programmeerbare logische (PL) structuur (Afbeelding 1). Naast de ingebouwde processors en een uitgebreid scala aan randapparatuur, biedt de Zynq SoC maximaal 2020 digitale signaalverwerkingsblokken of -plakken (DSP). Met behulp van deze resources kunnen ontwikkelaars de PL-structuur configureren in gespecialiseerde verwerkingsketens die nodig zijn om de doorvoer in complexe reken-intensieve algoritmen te versnellen.

Schema van Xilinx Zynq-7000 SoC

Afbeelding 1: de Xilinx Zynq-7000 SoC combineert een dual-core Arm Cortex-A9 processor, programmeerbare logische structuur en een uitgebreide set van randapparatuur en interfaces die in vele ingebedde toepassingen nodig zijn. (Bron afbeelding: Xilinx)

Integratie van de processors en PL-structuur zorgt niet alleen voor een vermindering van het aantal onderdelen, maar maakt het ook mogelijk bewerkingen over -chip bussen te laten plaatsvinden in plaats van via off-chip toegang. Deze integratie zorgt voor een verdere vereenvoudiging van de kritieke taak van het laden van de PL-structuur gedurende inschakel- of resetprocedures.

In een typisch, op een microcontroller gebaseerd, met een FPGA gebouwd systeem, moesten ontwikkelaars de procedure en beveiliging voor het laden van bitstreams die de FPGA programmeren beheren. Met de Zynq SoC voert een geïntegreerde processor de conventionele taken van een microcontroller uit, waaronder ook het beheren van de PL-structuur en andere on-chip randapparatuur. Het resultaat is dat het proces voor het laden van de FPGA meer lijkt op een conventionele startprocedure van een microcontroller dan op een traditionele FPGA-bitstreaminitialisatie.

Deze startprocedure vindt plaats via een korte opeenvolging van stappen die door een van de processors van Zynq wordt beheerd (Afbeelding 2). Bij inschakeling of reset begint de startprocedure wanneer een Zynq-processor een kort stukje code uit zijn alleen-lezen BootROM uitvoert om de actuele bootcode op te halen bij een opstartapparaat. Naast code voor het configureren van de processorsysteemcomponenten, bevat de bootcode ook de PL-bitstream en de gebruikerstoepassing. Wanneer het laden van de bootcode voltooid is, gebruikt de processor de meegestuurde bitstream om de PL te configureren. Nadat configuratie en PL-configuratie zijn voltooid, begint het apparaat met het uitvoeren van de applicatie die is opgenomen in de bootcode.

Afbeelding van opstartprocedure Xilinx Zynq-7000 SoC

Afbeelding 2: in een opstartprocedure die sterk lijkt op die van conventionele microcontrollers, voert een Xilinx Zynq-7000 SoC code uit vanuit Boot ROM die de bootloader laadt en uitvoert, die volgende fasen verwerkt, inclusief het gebruik van een in de bootcode verpakte bitstream om de programmeerbare logische structuur te configureren. (Bron afbeelding: Xilinx)

Zelfs met vereenvoudigde processen voor het laden van PL ontkwamen ontwikkelaars in het verleden niet aan het complexe FPGA-ontwikkelingsproces dat nodig was om de vereiste bitstreams te genereren. Voor ontwikkelaars die geïnteresseerd zijn in de prestaties die FPGA's kunnen leveren, vormt het conventionele FGPA-ontwikkelingsproces nog steeds een groot obstakel voor implementatie. Met zijn PYNQ-omgeving heeft Xilinx dit obstakel effectief uit de weg geruimd.

PYNQ-omgeving

In PYNQ zijn PL-bitstreams ingekapseld in kant-en-klare bibliotheken die overlays worden genoemd. Deze vervullen een rol die vergelijkbaar is met die van softwarebibliotheken in het ontwikkelingsproces en de uitvoeringsomgeving. Tijdens het opstartlaadproces configureren aan de vereiste overlays gekoppelde bitstreams de PL-structuur. Dit proces blijft echter transparant voor de ontwikkelaars, die de functionaliteit van de overlays kunnen gebruiken via de applicatie-programmeerinterface (API) van Python die aan iedere overlay is gekoppeld. Gedurende de ontwikkeling kunnen ontwikkelaars softwarebibliotheken en overlays naar behoefte combineren, waarbij ze de respectievelijke API's gebruiken om de toepassing te implementeren. Tijdens de uitvoering voert het processorsysteem de softwarebibliotheekcode zoals gebruikelijk uit, terwijl de PL-structuur de functionaliteit implementeert die in de overlay wordt geboden. Het resultaat is het soort versnelde prestaties dat nog steeds interessant blijft in op FPGA gebaseerde ontwerpen voor toepassingen die dag na dag veeleisender worden.

Zoals de naam al suggereert, buit PYNQ de verbeteringen in ontwikkelingsproductiviteit uit, die aan de Python-programmeertaal zijn gekoppeld. Python is uitgegroeid tot één van de belangrijkste talen, niet alleen vanwege zijn relatieve eenvoud, maar ook vanwege zijn grote en immer groeiende ecosysteem. Ontwikkelaars hebben een grote kans om de softwarebibliotheken te vinden die ze nodig hebben voor het ondersteunen van diensten of gespecialiseerde algoritmen in repositories (data-opslagplaatsen) van open-source Python-modules. Tegelijkertijd kunnen ontwikkelaars kritieke functies in C-taal implementeren omdat PYNQ de normale C-taal-implementatie van de Python-interpreter gebruikt. Deze implementatie biedt simpel toegang tot duizenden bestaande C-bibliotheken en vereenvoudigt het gebruik van C-taal-bibliotheken die de ontwikkelaar zelf aanlevert. Alhoewel ervaren ontwikkelaars PYNQ kunnen uitbreiden met gespecialiseerde hardware-overlays en C-taal-softwarebibliotheken, berust de kracht van PYNQ vooral in het feit dat het een uitermate productieve ontwikkelingsomgeving biedt voor iedere willekeurige ontwikkelaar die in staat is om een Pythonprogramma te bouwen.

PYNQ is zelf een open-source project en is gebaseerd op een ander open-source project, Jupyter notebook. Jupyter notebooks bieden een bijzonder doeltreffende omgeving voor het interactief verkennen van algoritmen en het prototyperen van complexe toepassingen in Python of één van de andere ondersteunde programmeertalen (momenteel zijn dat er meer dan 40). Een Jupyter notebook, ontwikkeld met goedkeuren van de gemeenschap onder het Project Jupyter, combineert regels van uitvoerbare code met beschrijvende tekst en grafische elementen. Dit stelt afzonderlijke ontwikkelaars in staat om hun voortgang op meer doeltreffende wijze te documenteren zonder naar een andere ontwikkelingsomgeving te gaan. Een ontwikkelaar kan bijvoorbeeld een notebook gebruiken dat een aantal regels code, benodigd om de gegevens weer te geven combineert met de grafische elementen die door de code zijn gegenereerd (Afbeelding 3).

Afbeelding van Jupyter notebook van een Xilinx sample repository

Afbeelding 3: een Jupyter notebook van een Xilinx sample repository combineert beschrijvende tekst, uitvoerbare code en een aan een toepassing geassocieerde uitvoer. (Bron afbeelding: Xilinx)

Dat het notebook code, uitvoer en beschrijvende tekst kan bevatten, is mogelijk omdat een Jupyter notebook een 'live' document is dat wordt onderhouden in een interactieve, door een Jupyter notebookserver geboden ontwikkelingsomgeving (Afbeelding 4). In een Jupyter-sessie rendert de server het notebookbestand in een conventionele Webbrowser met behulp van HTTP en een combinatie van HTTP- en Websockets-protocollen voor de statische en dynamische content in het gerenderde document. Aan het back-end communiceert de server met een kernel voor codeuitvoering met behulp van het open-source ZeroMQ (ØMQ) berichtenprotocol.

Schema van Jupyter sessie-workflow

Afbeelding 4: in een Jupyter-sessie rendert een notebookserver de inhoud van een notebookbestand naar een Webbrowser via interactie met een back-end kernel die de code uitvoert. (Bron afbeelding: Project Jupyter)

In de bewerkingsmodus kan de gebruiker de tekst en code wijzigen. Op zijn beurt werkt de server het overeenkomstige notebookbestand, een tekstbestand met een reeks JSON sleutel-/waardeparen, bij. In de Jupyter-omgeving worden deze paren cellen genoemd. De webbrowserweergave van het Jupyter-notebook die eerder is weergegeven bevat bijvoorbeeld enkele cellen voor code en markdown-tekst (Lijst 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()"
   ]
  },

Lijst 1: een Jupyter-notebook is een tekstbestand dat een reeks JSON sleutel-/waardeparen bevat die delen code, markup en uitvoer bevatten, zoals deze, die overeenkomen met de gerenderde pagina van Afbeelding 3. Merk op dat de string die overeenkomt met de .png afbeelding in die figuur hier is ingekort om de presentatie duidelijker te maken. (Bron code: Xilinx)

Naast de documentatiefuncties berust de kracht van de Jupyter-omgeving in zijn vermogen om codecellen op interactieve wijze uit te voeren. Ontwikkelaars selecteren eenvoudigweg de cel die hen interesseert in hun browser (blauwe rand in Afbeelding 3) en klikken op de run-knop in het Jupytermenu bovenin het browservenster. De Jupyter notebookserver geeft de bijbehorende codecel op zijn beurt af aan een kernel voor codeuitvoering, de interactieve Python (IPython)-kernel in de PYNQ-omgeving. Na het uitvoeren van de code werkt de server op asynchrone wijze zowel de gerenderde Webpagina als het notebookbestand bij met alle door de kernel gegenereerde uitvoer.

PYNQ breidt deze zelfde benadering uit tot op FPGA gebaseerde ontwikkeling door het Jupyter framework, inclusief IPython kernel en notebook-webserver in te bedden in de Arm-processors van Zynq SoC. De PYNQ-Pythonmodule in de omgeving voorziet programmeurs van de Python API die ze nodig hebben om toegang te krijgen tot PYNQ-diensten in Python-programma's.

FPGA-ontwikkelingsomgeving

De Digilent PYNQ-Z1 ontwikkelingskit, specifiek ontworpen om PYNQ te ondersteunen, laat ontwikkelaars snel beginnen met het verkennen van door FPGA versnelde toepassingen door eenvoudig het beschikbare via PYNQ opstartbare Linux beeld te laden. Het PYNQ-Z1 bord combineert een XC7Z020 Zynq SoC met 512 megabyte (Mbyte) RAM, 16 Mbyte flash en een microSD slot van Xilinx voor extra extern flash-geheugen. Naast schakelaars, knoppen, leds en meerdere ingangs-/uitgangspoorten, biedt het bord ook connectors voor uitbreiding naar hardware van derden via de Digilent Pmod (perifere module)-interface en via Arduino-shields en Digilent chipKIT-shields. Het bord presenteert ook de analoog-digitaalomzetter (ADC) van Zynq SoC, de XADC genaamd, als zes enkelzijdige analoge ingangspoorten of vier differentiële analoge ingangspoorten. Digilent levert ook de separate PYNQ-Z1 productiviteitskit bestaande uit een voeding, een micro-USB-kabel, een microSD-kaart met voorgeladen PYNQ afbeelding en een Ethernetkabel voor het updaten of toevoegen van Pythonmodules.

De volledige mogelijkheden van SoC en het bord zijn direct toegankelijk voor de ontwikkelaar via een Jupyter-notebook. Toegang krijgen tot de Pmod-interface van het bord om de ADC te lezen en de waarden van de digitaal-analoogomzetter (DAC) in een loopback-test te schrijven vereist bijvoorbeeld slechts enkele regels code (Afbeelding 5). Na het importeren van de vereiste Pythonmodules wordt de SoC PL geïnitialiseerd met een “basis”-overlay (cel twee in Afbeelding 5). Net als bij een conventioneel bordondersteuningspakket, biedt deze basis-overlay toegang tot de randapparatuur van het bord.

Afbeelding van Jupyter notebook dat deel uitmaakt van de Xilinx sample repository

Afbeelding 5: een Jupyter notebook dat deel uitmaakt van de Xilinx sample repository demonstreert het eenvoudige ontwerppatroon voor toegang tot hardwareservices voor ingangs-/uitgangstransacties. (Bron afbeelding: Xilinx)

Het enige wat de ontwikkelaar moet doen is geïmporteerde modules oproepen om de waarden te lezen en te schrijven (cel drie in de Afbeelding). In het getoonde voorbeeldnotebook geeft de notebookserver de cellen een voor een af en werkt het notebook bij met de gegenereerde resultaten. In dit geval is de enige uitvoerwaarde 0,3418, maar verschijnen eventuele uitvoeringsfouten als normale Python-traceback-stacks, uitgelijnd met de bijbehorende codecellen.

Complexe toepassingen bouwen

In combinatie met het brede scala aan beschikbare Pythonmodules maskeert deze bedrieglijk eenvoudige benadering van embedded-toepassingen een krachtig platform voor het snel implementeren van complexe, reken-intensieve toepassingen. Ontwikkelaars kunnen bijvoorbeeld snel een webcam voor gezichtsherkenning implementeren met behulp van de PYNQ-Z1 HDMI-ingang en de populaire OpenCV-bibliotheek voor computer vision. Na het laden van de basis-overlay en webcam-interface wordt een OpenCV camera-object videoIn geïnitialiseerd (Afbeelding 6). Het aflezen van videobeelden is vervolgens kinderlijk eenvoudig: door oproepen van videoIn.read(), returning frame_vga in dit voorbeeld.

Afbeelding van Jupyter notebook dat deel uitmaakt van de Xilinx sample repository

Afbeelding 6: een Jupyter notebook dat deel uitmaakt van de Xilinx sample repository laat zien hoe ontwikkelings snel een webcam-gezichtsherkenningssysteem kunnen bouwen door hardwarebronnen van het PYNQ-Z1 ontwikkelingsbord te combineren met krachtige beeldverwerkingsfuncties die beschikbaar zijn in de OpenCV bibliotheek (cv2). (Bron afbeelding: Xilinx)

In een volgende stap, als aparte cel in het notebook beheerd, worden OpenCV (cv2) classificatorobjecten aangemaakt met behulp van vooringestelde criteria en kaders toegevoegd om functies te identificeren (in dit voorbeeld groen voor ogen en blauw voor gezichten). In een ander paar cellen wordt de toepassing voltooid na weergave van de uitvoer met behulp van de HDMI-uitgang van het bord (Afbeelding 7).

Afbeelding van laatste cellen in Xilinx webcam-gezichtsherkenning-notebook

Afbeelding 7: de laatste cellen in het Xilinx webcam-gezichtsherkenning-notebook demonstreert het gebruik van OpenCV classificators; de resultaten hiervan worden gebruikt om kaders toe te voegen aan de originele beelden en weergegeven met behulp van de HDMI-uitgangspoort van het PYNQ-Z1-ontwikkelingsbord. (Bron afbeelding: Xilinx)

Het vermogen om interactief complexe software te bouwen en te testen en inzichten hierover te delen, heeft Jupyter-notebooks bijzonder populair gemaakt onder wetenschappers en ingenieurs die zich bezighouden met het optimaliseren van algoritmen voor kunstmatige-intelligentie-toepassingen. Naarmate het werk vordert, toont het notebook niet alleen de code en de bijpassende uitvoer, maar ook de analyse van de ontwikkelaars over de resultaten, wat een soort van 'computationeel verhaal' oplevert, dat kan worden gedeeld met teamleden en collega's.

Ontwikkelaars dienen echter wel te begrijpen dat het onwaarschijnlijk is dat notebooks repositories kunnen zijn voor meer op productie gerichte projecten. Het feit dat ze grote hexadecimale gecodeerde reeksen voor afbeeldingsgegevens bevatten (zie de ingekorte sectie in Lijst 1) laat bijvoorbeeld niet alleen de documentgrootte toenemen, maar kan ook problemen opleveren voor de differentiemethoden die door standaard bronversiebeheersystemen worden gebruikt. De verweving van code en niet-functionele tekst kan migratie van code, die in vroege analytische stadia is gemaakt, naar ontwikkelingsprocessen op productieniveau verder bemoeilijken. Voor codeverkenning en snelle prototypering bieden Jupyter notebooks echter een krachtige ontwikkelingsomgeving.

Conclusie

FPGA's bieden de prestatieverbetering die nodig is om te voldoen aan de steeds hogere eisen van embedded-systemen voor IoT, computer vision, industriële automatisering, automotive en nog veel meer. Alhoewel conventionele methoden voor FPGA-ontwikkeling voor veel ontwikkelaars een obstakel zijn gebleven, biedt de opkomst van de op Python gebaseerde PYNQ-ontwikkelingsomgeving, gebaseerd op Jupyter notebooks, een doeltreffend alternatief. Door een ontwikkelingsbord te gebruiken dat specifiek is ontworpen om PYNQ te ondersteunen, kunnen ook ontwikkelaars met weinig ervaring op het gebied van FPGA's snel ontwerpen implementeren die ten volle van de prestaties van FPGA's profiteren, om reken-intensieve toepassingen sneller te maken.

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.

Achtergrondinformatie over deze auteur

Image of Stephen Evanczuk

Stephen Evanczuk

Stephen Evanczuk heeft meer dan 20 jaar ervaring in het schrijven voor en over de elektronicasector met betrekking tot heel wat onderwerpen, waaronder hardware, software, systemen en toepassingen zoals het IoT. Hij behaalde zijn filosofiediplomain neurowetenschappen over neuronale netwerken en werkte in de ruimtevaartsector op massaal verspreide veilige systemen en algoritmeversnellingsmethoden. Wanneer hij geen artikels over technologie en techniek schrijft, werkt hij aan toepassingen voor “deep learning” voor herkennings- en aanbevelingssystemen.

Over deze uitgever

De Noord-Amerikaanse redacteurs van DigiKey