Creare e programmare rapidamente progetti basati su FPGA con Python e Jupyter Notebook

Di Stephen Evanczuk

Contributo di Editori nordamericani di DigiKey

In genere i progettisti si affidavano a FPGA (gate array programmabili sul campo) per accelerare le prestazioni in progetti hardware per applicazioni di calcolo complesse quali la visione artificiale, le comunicazioni, i sistemi embedded industriali e, sempre più, Internet delle cose (IoT). Dato però che la programmazione tradizionale di FPGA si è rivelata estremamente complessa, fino ad oggi sono stati spinti a cercare soluzioni di elaborazione alternative.

L'emergere dell'ambiente di sviluppo Python Productivity for Zynq (PYNQ) basato su Jupyter Notebook affronta il problema della programmabilità degli FPGA. Usando una scheda di sviluppo concepita espressamente per supportare PYNQ, gli sviluppatori poco esperti di FPGA possono implementare rapidamente progetti che sfruttano tutte le prestazioni degli FPGA per accelerare applicazioni di calcolo complesse.

Questo articolo descriverà il tipico approccio all'FPGA prima di presentare e illustrare come partire da una scheda di sviluppo offerta da Digilent, che rappresenta una potente alternativa open source, per accelerare lo sviluppo di sistemi basati su FPGA.

Perché gli FPGA?

I progettisti che devono impiegare algoritmi complessi e che richiedono molti calcoli spesso si affidano agli FPGA per accelerare l'esecuzione senza sacrificare la poca potenza disponibile. Gli FPGA sono emersi come piattaforma dominante per accelerare gli algoritmi di intelligenza artificiale in sistemi di edge-computing (vedere "Utilizzare gli FPGA per creare applicazioni di visione embedded ad alte prestazioni con l'apprendimento automatico").

Studiati espressamente per applicazioni embedded, i dispositivi system-on-chip (SoC) FPGA più avanzati integrano una struttura di logica programmabile (PL) con un microcontroller. Ad esempio, il SoC Zynq-7000 di Xilinx combina nella sua struttura di logica programmabile (PL) integrata un sistema processore dual core Arm® Cortex®-A9 con un massimo di 444.000 celle logiche (Figura 1). Oltre a processori incorporati e a un ricco corredo di periferiche, il SoC Zynq offre un massimo di 2.020 blocchi di elaborazione dei segnali digitali (DSP), o slice. Grazie a queste risorse, gli sviluppatori possono configurare la struttura di PL in catene di elaborazione specializzate necessarie per accelerare il throughput in algoritmi complessi e ad alta intensità di calcoli.

Schema del Soc Zynq-7000 di Xilinx

Figura 1: Il Soc Zynq-7000 di Xilinx combina un processore dual core Arm Cortex-A9, una struttura di logica programmabile e un ricco corredo di periferiche e interfacce necessarie in numerose applicazioni embedded. (Immagine per gentile concessione di Xilinx)

Oltre a ridurre il numero dei componenti, l'integrazione dei processori e della struttura di PL consente l'esecuzione delle operazioni attraverso bus su chip anziché tramite l'accesso non su chip. Questa integrazione semplifica ulteriormente l'attività critica di caricamento della struttura di PL durante le sequenze di accensione o di ripristino.

In un tipico sistema basato su microcontroller costruito con un FPGA, gli sviluppatori dovevano gestire la sequenza e la sicurezza per caricare bitstream di programmazione dell'FPGA. Con il SoC Zynq, un processore integrato esegue le attività tradizionali di un microcontroller, compresa la gestione della struttura di PL e di altre periferiche su chip. Di conseguenza, il processo di caricamento FPGA è molto più simile a quello del boot di un microcontroller convenzionale rispetto a un'inizializzazione di bitstream FPGA tradizionale.

Questo processo di boot avviene tramite una breve sequenza di passaggi gestiti da uno dei processori Zynq (Figura 2). All'accensione o al ripristino, il processo di boot si attiva quando un processore Zynq esegue un piccolo pezzo di codice dalla sua BootROM di sola lettura per prelevare il codice boot effettivo da un dispositivo di boot. Oltre al codice per la configurazione dei componenti del sistema del processore, il codice boot include il bitstream PL e l'applicazione utente. Al termine del caricamento del codice boot, il processore usa il bitstream incluso per configurare la PL. Dopo la configurazione della PL, il dispositivo inizia a eseguire l'applicazione inclusa nel codice boot.

Immagine della sequenza di boot del SoC Zynq-7000 di Xilinx

Figura 2: In una sequenza di boot simile ai microcontroller tradizionali, un SoC Zynq-7000 di Xilinx esegue il codice dalla BootROM che carica ed esegue il bootloader, che a sua volta gestisce le fasi successive utilizzando un bitstream inserito nel codice boot per configurare la struttura di logica programmabile. (Immagine per gentile concessione di Xilinx)

Nonostante l'elaborazione semplificata del caricamento della PL, in passato gli sviluppatori si sono dovuti confrontare con il complesso processo di sviluppo dell'FPGA necessario per generare i bitstream richiesti. Per chi sperava di sfruttare le prestazioni dell'FPGA, il suo processo di sviluppo tradizionale è rimasto un grave ostacolo all'implementazione. Con il suo ambiente PYNQ, Xilinx ha rimosso in modo efficace questa barriera.

Ambiente PYNQ

In PYNQ, i bitstream di PL sono incapsulati in librerie precostruite chiamate overlay, che svolgono un ruolo simile alle librerie software nel processo di sviluppo e nell'ambiente di esecuzione. Durante il processo di bootload, i bitstream associati agli overlay richiesti configurano la struttura di PL. Tuttavia, questo processo rimane trasparente agli sviluppatori che sfruttano le funzionalità degli overlay tramite l'API Python associata a ciascuno. Durante lo sviluppo, gli ingegneri possono combinare le librerie software e gli overlay in base alle esigenze, utilizzando le rispettive API per implementare l'applicazione. Nel corso dell'esecuzione, il sistema del processore esegue il codice della libreria software come d'abitudine, mentre la struttura di PL implementa le funzionalità fornite nell'overlay. Le prestazioni accelerate risultanti continuano a stimolare l'interesse per progetti basati su FPGA per applicazioni sempre più esigenti.

Come suggerito dal nome, PYNQ sfrutta i guadagni di produttività dello sviluppo associati al linguaggio di programmazione Python. Python è emerso come uno dei linguaggi preferiti non solo per la sua relativa semplicità ma anche per il suo esteso ecosistema in espansione. È probabile che gli sviluppatori trovino le librerie software di cui hanno bisogno per supportare i servizi o gli algoritmi specializzati nei repository dei moduli Python open-source. Allo stesso tempo, possono implementare le funzioni critiche in linguaggio C, perché PYNQ si serve dell'implementazione del comune linguaggio C dell'interprete Python. Questa implementazione consente di accedere facilmente a migliaia di librerie C esistenti e semplifica l'uso di quelle fornite dagli sviluppatori. Anche se gli sviluppatori esperti possono estendere PYNQ con overlay hardware specializzati e librerie software di linguaggio C, il punto di forza di PYNQ è la sua capacità di assicurare un ambiente di sviluppo altamente produttivo a qualsiasi sviluppatore in grado di costruire un programma Python.

PYNQ, che è di per sé un progetto open-source, si fonda su un altro progetto open-source, Jupyter Notebook. Jupyter Notebook offre un ambiente particolarmente efficiente per esplorare in modo interattivo gli algoritmi e prototipare applicazioni complesse in Python o in uno qualsiasi degli oltre 40 linguaggi di programmazione attualmente supportati. Sviluppato con il consenso della comunità nel quadro del progetto Jupyter, Jupyter Notebook combina linee di codice eseguibile con testo descrittivo e grafica. Questa capacità consente ai singoli sviluppatori di documentare più incisivamente i propri progressi senza passare a un altro ambiente di sviluppo. Ad esempio, uno sviluppatore può usare un Notebook che combina alcune righe di codice necessarie per vedere i dati con il grafico generato dal codice (Figura 3).

Immagine di Jupyter Notebook da un repository di esempi di Xilinx

Figura 3: Un Jupyter Notebook presente in un repository di esempi di Xilinx combina testo descrittivo, codice eseguibile e un output associato a un'applicazione. (Immagine per gentile concessione di Xilinx)

La capacità di contenere codice, output e testo descrittivo è resa possibile dal fatto che Jupyter Notebook è un documento dinamico, mantenuto in un ambiente di sviluppo interattivo fornito da un server Jupyter Notebook (Figura 4). In una sessione Jupyter, il server effettua il rendering del file Notebook in un browser Web tradizionale utilizzando HTTP, e una combinazione di protocolli HTTP e Websocket per il contenuto statico e dinamico nel documento sottoposto a rendering. Sul backend, il server comunica con un kernel di esecuzione del codice tramite il protocollo di messaggistica open-source ZeroMQ (ØMQ).

Schema del flusso di lavoro della sessione Jupyter

Figura 4: In una sessione Jupyter, un server Notebook effettua il rendering del contenuto di un file Notebook in un browser Web mentre interagisce con un kernel backend che esegue il codice. (Immagine per gentile concessione di Project Jupyter)

In modalità di editing, l'utente può modificare il testo e il codice. A sua volta, il server aggiorna il rispettivo file Notebook, che è un file di testo comprendente una serie di coppie di chiave/valore JSON. Nell'ambiente Jupyter queste coppie sono chiamate celle. Ad esempio, la vista del browser Web del Jupyter Notebook mostrato in precedenza comprende alcune celle per il codice e il testo markdown (Listato 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()"
   ]
  },

Listato 1: Un Jupyter Notebook è un file di testo che contiene una serie di coppie di chiave/valore JSON con sezioni di codice, markup e output come questi, che corrispondono alla pagina sottoposta a rendering riportata nella Figura 3. Tenere presente che qui, a fini di presentazione, la stringa corrispondente all'immagine .png in quella figura è stata troncata. (Codice per gentile concessione di Xilinx)

A parte le funzioni di documentazione, la forza dell'ambiente Jupyter risiede nella sua capacità di eseguire interattivamente le celle di codice. Gli sviluppatori devono semplicemente selezionare la cella desiderata nel proprio browser (bordo blu nella Figura 3) e fare clic sul pulsante di esecuzione nel menu Jupyter in cima alla finestra del browser. Il server Jupyter Notebook, a sua volta, consegna la cella del codice corrispondente a un kernel di esecuzione del codice che è il kernel Python interattivo (IPython) nell'ambiente PYNQ. Dopo l'esecuzione del codice, il server aggiorna in modo asincrono sia la pagina Web sottoposta a rendering, sia il file Notebook con qualsiasi output generato dal kernel.

PYNQ estende lo stesso approccio allo sviluppo basato su FPGA incorporando il framework Jupyter, compreso il kernel IPython e il server Web Notebook nei processori Arm del SoC Zynq. Il modulo pynq Python incluso nell'ambiente offre ai programmatori l'API Python necessaria per accedere ai servizi PYNQ nei programmi Python.

Ambiente di sviluppo di FPGA

Progettato espressamente per supportare PYNQ, il kit di sviluppo PYNQ-Z1 di Digilent consente agli sviluppatori di iniziare rapidamente a esplorare le applicazioni accelerate da FPGA semplicemente caricando il linguaggio Linux disponibile avviabile da PYNQ. La scheda PYNQ-Z1 combina un SoC Zynq XC7Z020 di Xilinx con 512 Mbyte di RAM, 16 Mbyte di flash e uno slot microSD per memoria flash esterna supplementare. Oltre a interruttori, pulsanti, LED e diverse porte di ingresso/uscita, la scheda dispone anche di connettori per l'espansione a hardware di terze parti tramite l'interfaccia Digilent Pmod (modulo periferico), shield Arduino e shield chipKIT di Digilent. La scheda comprende anche il convertitore analogico/digitale (ADC) del SoC Zynq, denominato XADC, oltre a sei porte di ingresso analogico a terminazione singola o quattro porte di ingresso analogico differenziale. Digilent fornisce anche il kit di produttività PYNQ-Z1 separato che comprende un alimentatore, un cavo Micro USB, una scheda microSD precaricata con un'immagine PYNQ e un cavo Ethernet per aggiornare o aggiungere moduli Python.

Lo sviluppatore può accedere facilmente a tutte le capacità del SoC e della scheda tramite un Jupyter Notebook. Ad esempio, per accedere all'interfaccia Pmod della scheda per leggere i valori dell'ADC e scrivere i valori del convertitore digitale/analogico (DAC) in un test di loopback servono solo poche righe di codice (Figura 5). Dopo aver importato i moduli Python richiesti, la PL del SoC viene inizializzata con un overlay di "base" (cella due nella Figura 5). Analogamente a un BSP (board support package) tradizionale, questo overlay di base fornisce l'accesso alle periferiche della scheda.

Immagine del Jupyter Notebook incluso nel repository di esempi di Xilinx

Figura 5: Un Jupyter Notebook incluso nel repository di esempi di Xilinx dimostra la semplicità di progettazione associata all'accesso dei servizi hardware per transazioni di ingresso/uscita. (Immagine per gentile concessione di Xilinx)

È sufficiente che gli sviluppatori richiamino i moduli importati, per leggere e scrivere i valori (cella tre nella Figura). Nel Notebook di esempio mostrato, il server Notebook emette ogni cella in sequenza e aggiorna il Notebook con i risultati generati. In questo caso, l'unico valore di uscita è 0.3418, ma qualsiasi errore di esecuzione comparirà come i normali stack di traceback di Python in linea con le rispettive celle del codice.

Costruire applicazioni complesse

Abbinato all'ampia serie di moduli Python disponibili, questo approccio apparentemente semplice allo sviluppo di applicazioni embedded maschera una piattaforma potente per implementare rapidamente applicazioni di calcolo complesse. Ad esempio, gli sviluppatori possono implementare rapidamente una webcam di riconoscimento facciale usando l'ingresso HDMI PYNQ-Z1 e la diffusa libreria di visione artificiale OpenCV. Dopo aver caricato l'overlay di base e l'interfaccia webcam, gli sviluppatori inizializzano un oggetto videocamera OpenCV videoIn (Figura 6). Per leggere le immagini video è sufficiente una chiamata a videoIn.read(), che in questo esempio restituisce frame_vga.

Immagine del Jupyter Notebook incluso nel repository di esempi di Xilinx

Figura 6: Un Jupyter Notebook dal repository di esempi di Xilinx mostra come gli sviluppatori possano creare rapidamente un sistema di riconoscimento facciale per webcam combinando le risorse hardware della scheda di sviluppo PYNQ-Z1 con le potenti funzioni di elaborazione immagini disponibili nella libreria OpenCV (cv2). (Immagine per gentile concessione di Xilinx)

In una fase successiva, gestita come cella separata nel Notebook, gli sviluppatori creano gli oggetti del classificatore OpenCV (cv2) usando i criteri preimpostati e aggiungono riquadri di delimitazione per identificare le caratteristiche (verde per gli occhi e blu per i volti, in questo esempio). In un'altra coppia di celle, l'applicazione si completa dopo aver visualizzato il risultato usando l'uscita HDMI della scheda (Figura 7).

Immagine delle celle finali nel Notebook di riconoscimento facciale tramite webcam di Xilinx

Figura 7: Le celle finali nel Notebook di riconoscimento facciale tramite webcam di Xilinx dimostrano l'uso dei classificatori OpenCV, i cui risultati vengono utilizzati per aggiungere riquadri di delimitazione alle immagini originali e visualizzati tramite la porta di uscita HDMI della scheda di sviluppo PYNQ-Z1. (Immagine per gentile concessione di Xilinx)

La possibilità di creare, testare e condividere interattivamente discussioni su software complesso ha fatto di Jupyter Notebook uno degli strumenti preferiti di scienziati e ingegneri impegnati a ottimizzare gli algoritmi per applicazioni di intelligenza artificiale. Più il lavoro evolve, Notebook non solo mostra il codice e il suo output, ma anche l'analisi degli sviluppatori sui risultati, fornendo una sorta di narrativa computazionale che può essere condivisa con i membri del team e altri colleghi.

Gli sviluppatori devono però capire che difficilmente i Notebook saranno i repository giusti per sforzi più orientati alla produzione. Ad esempio, l'inclusione di lunghe stringhe di codice esadecimale per i dati delle immagini (vedere la sezione troncata nel Listato 1) non solo aumenta le dimensioni del documento, ma può anche complicare i metodi di differenziazione usati dai tipici sistemi di controllo della versione sorgente. L'interlacciamento di codice e testo non funzionale può rendere ancora più complessa la migrazione del codice creato nelle prime fasi analitiche verso i processi di sviluppo a livello di produzione. Per l'esplorazione del codice e una prototipazione rapida, tuttavia, i Jupyter Notebook offrono un potente ambiente di sviluppo.

Conclusione

Gli FPGA assicurano l'incremento delle prestazioni necessario per rispondere alle crescenti richieste di sistemi embedded progettati per IoT, visione artificiale, automazione industriale, settore automotive e altri. Sebbene le tradizionali metodologie di sviluppo degli FPGA siano rimaste un ostacolo per molti sviluppatori, l'emergere dell'ambiente di sviluppo PYNQ fondato su Python e su Jupyter Notebook è un'alternativa efficace. Usando una scheda di sviluppo concepita espressamente per supportare PYNQ, gli sviluppatori poco esperti di FPGA possono implementare rapidamente progetti che sfruttano tutte le prestazioni degli FPGA per accelerare applicazioni di calcolo complesse.

DigiKey logo

Esonero della responsabilità: le opinioni, le convinzioni e i punti di vista espressi dai vari autori e/o dai partecipanti al forum su questo sito Web non riflettono necessariamente le opinioni, le convinzioni e i punti di vista di DigiKey o le sue politiche.

Informazioni su questo autore

Image of Stephen Evanczuk

Stephen Evanczuk

Stephen Evanczuk ha più di 20 anni di esperienza come autore sull'industria elettronica e ha scritto su una vasta gamma di argomenti tra cui hardware, software, sistemi e applicazioni, incluso l'IoT. Ha ricevuto un Ph.D. in neuroscienze sulle reti neuronali e ha lavorato nel settore aerospaziale su sistemi di sicurezza ampiamente distribuiti e sui metodi di accelerazione algoritmica. Attualmente, quando non scrive articoli su tecnologia e ingegneria, lavora su applicazioni di deep learning per i sistemi di riconoscimento e di raccomandazione.

Informazioni su questo editore

Editori nordamericani di DigiKey