O PYNQ é um projeto de código aberto da AMD (antiga Xilinx) que facilita o uso de plataformas de Adaptive Computing1. Usando a linguagem Python, os notebooks Jupyter e o vasto ecossistema de bibliotecas Python, projetistas podem explorar os benefícios da lógica programável e dos microprocessadores para construir sistemas eletrônicos mais capazes.

A sigla PYNQ vem de Python Productivity for Zynq, ou seja, produtividade em Python para o Zynq. A ideia central é simples: o circuito programável da FPGA é disponibilizado como uma biblioteca de hardware chamada overlay, que pode ser carregada em tempo de execução e controlada por Python, sem que o usuário precise dominar a programação RTL em VHDL ou Verilog para usá-la2.

O PYNQ pode ser usado para criar aplicações de alto desempenho com:

  • execução paralela em hardware;
  • processamento de vídeo a alta taxa de quadros;
  • algoritmos acelerados por hardware;
  • processamento de sinais em tempo real;
  • IO de alta largura de banda;
  • controle de baixa latência.

Neste material usamos como base de exemplo a placa PZ7020, construída em torno do SoC Zynq-7020 (xc7z020). Vale destacar que o PYNQ não se restringe a ela. Há diversas outras placas compatíveis, como as famílias PYNQ-Z1/Z2, Zynq UltraScale+ (ZCU104/ZCU111), Kria e RFSoC. O fluxo descrito aqui se aplica, com pequenos ajustes de part number e de configuração do PS, a qualquer placa com suporte ao PYNQ3.

Este texto cobre o fluxo completo de uso da placa:

  1. Preparação do cartão SD e inicialização do sistema;
  2. Acesso ao terminal pela porta serial;
  3. Configuração de rede para acesso via Ethernet;
  4. Acesso à interface Jupyter Notebook;
  5. Compartilhamento de arquivos via Samba;
  6. Criação de um overlay no Vivado e implantação na placa.

1. O que você vai precisar

Antes de começar, separe os itens abaixo. A lista vale para qualquer placa PYNQ, mudando apenas os conectores específicos de cada modelo:

  • a placa de desenvolvimento (no nosso exemplo, a PZ7020);
  • um cartão Micro SD (mínimo de 8 GB, recomendado até 32 GB);
  • um cabo Ethernet (de preferência Gigabit);
  • um cabo USB para a interface JTAG/UART da placa (na PZ7020 é USB Tipo-C);
  • a fonte de alimentação da placa;
  • um computador com navegador compatível (Chrome, Firefox ou Safari);
  • um software de terminal serial (minicom, PuTTY, MobaXterm ou SecureCRT).

2. Preparação do hardware

Gravação do cartão SD

A placa inicializa a partir de um cartão SD que contém a imagem do sistema operacional (Linux mais Jupyter). A imagem oficial está disponível no repositório do projeto PYNQ e, no caso da PZ7020, também é distribuída pelo fabricante. O arquivo de imagem usado no nosso exemplo é o pzpynq-2.6.0.img, baseado em Ubuntu 18.04. Outras placas e versões seguem a mesma lógica, apenas com nomes de arquivo diferentes.

  1. Baixe a imagem .img do sistema PYNQ correspondente à sua placa.
  2. Grave-a no cartão SD. A ferramenta varia conforme o sistema operacional do computador:
    • no Windows, é comum usar o balenaEtcher ou o Win32DiskImager;
    • no Linux, você pode usar o comando dd ou também o balenaEtcher.
  3. Insira o cartão gravado no slot da placa.

A imagem do PYNQ ocupa vários gigabytes, então a gravação pode levar alguns minutos. Confirme que o cartão selecionado é realmente o cartão SD, pois todo o conteúdo dele será apagado.

Configuração do modo de boot

Para que a placa inicialize a partir do cartão SD, é necessário posicionar o jumper de boot corretamente:

  • localize o jumper de modo de boot na placa (na PZ7020 é o JP4, identificado como BOOT MODE);
  • posicione-o na opção SD, e não em JTAG ou QSPI.

Conexões físicas

  • Conecte o cabo USB ao conector JTAG/UART da placa (na PZ7020, o conector PROG/UART, via USB Tipo-C). Ele serve tanto para alimentação quanto para o terminal serial.
  • Conecte o cabo Ethernet na porta PS Ethernet, que é a porta ligada ao processador, e não à lógica programável.
  • Ligue a placa. O LED de power deve acender e, após alguns segundos, os LEDs de status indicam que o sistema foi inicializado.

3. Acesso via terminal serial

Antes de configurar a rede, é possível acompanhar o boot e acessar o terminal da placa diretamente pela porta serial (USB UART). Isso é útil para depurar problemas de inicialização e para descobrir o endereço IP da placa.

Instalação do minicom

No exemplo a seguir usamos o minicom, mas qualquer terminal serial funciona. Em um computador Ubuntu, instale com:

Terminal window
1
sudo apt install minicom

Conexão serial

Com o cabo USB já conectado ao conector JTAG/UART da placa, abra o terminal serial:

Terminal window
1
sudo minicom -D /dev/ttyUSB0 -b 115200

Os parâmetros de comunicação são:

ParâmetroValor
Taxa de baud115200
Bits de dados8
Paridadenenhuma
Bits de parada1
Controle de fluxonenhum

Essa combinação costuma ser abreviada como 115200 8N1, sem controle de fluxo. Após o boot, o sistema faz o login automaticamente. Caso precise entrar manualmente, as credenciais padrão são:

  • Usuário: xilinx
  • Senha: xilinx

4. Configuração de rede

A placa PYNQ vem com o endereço IP estático 192.168.2.99. Se você ligar a placa em um roteador com DHCP, ela também recebe um segundo endereço dinâmico, e os dois funcionam. Para um acesso direto entre computador e placa, sem roteador, basta configurar o computador host com um endereço na mesma sub-rede (192.168.2.x/24).

Os comandos a seguir (ip, Netplan) são específicos de Linux/Ubuntu. Em outro sistema operacional o conceito é o mesmo, apenas a forma de configurar o IP muda.

Configuração temporária

Este método é rápido, mas o endereço é perdido ao reiniciar o computador. Primeiro, identifique a interface Ethernet conectada à placa:

Terminal window
1
ip a

Procure pela interface ligada ao cabo (por exemplo enp4s0 ou eth0). Em seguida, atribua o endereço IP:

Terminal window
1
sudo ip addr add 192.168.2.1/24 dev enp4s0
2
sudo ip link set enp4s0 up

Configuração permanente via Netplan

Para que o endereço IP persista após reinicializações, configure o Netplan, gerenciador de rede padrão no Ubuntu 18.04 em diante. Crie ou edite o arquivo de configuração:

Terminal window
1
sudo nano /etc/netplan/01-netcfg.yaml

Adicione o seguinte conteúdo, ajustando o nome da interface conforme necessário:

1
network:
2
version: 2
3
ethernets:
4
enp4s0:
5
addresses:
6
- 192.168.2.1/24
7
dhcp4: false

Por segurança, o Netplan exige que o arquivo seja legível apenas pelo root:

Terminal window
1
sudo chmod 600 /etc/netplan/01-netcfg.yaml

Aplique as configurações:

Terminal window
1
sudo netplan apply

Configuração de nome de host (opcional)

Esta etapa é totalmente opcional. Ela serve apenas para dar um nome amigável (por exemplo pynq.local) à placa, em vez de digitar o IP. Você pode pular esta seção e acessar a placa diretamente pelo IP (http://192.168.2.99:9090), tudo funcionará da mesma forma.

Para não precisar memorizar o endereço IP da placa, é possível associar um nome de host ao IP. Edite o arquivo /etc/hosts:

Terminal window
1
sudo nano /etc/hosts

Adicione a seguinte linha ao final do arquivo:

1
192.168.2.99 pynq.local pynq

Após salvar, a placa estará acessível pelos seguintes endereços:

EndereçoDescrição
http://192.168.2.99:9090Endereço IP direto
http://pynq.local:9090Nome de host local
http://pynq:9090Alias curto

5. Acesso ao Jupyter Notebook

Com a rede configurada, abra um navegador e acesse o endereço da placa na porta 9090:

1
http://192.168.2.99:9090

Se você configurou o nome de host ou está usando um roteador, também pode usar http://pynq:9090. A senha padrão é xilinx.

Depois de entrar, você verá a aba de arquivos do Jupyter, semelhante a um explorador de arquivos. O diretório de trabalho padrão é /home/xilinx/jupyter_notebooks, e é nele que ficam os exemplos prontos e os notebooks que você criar.

O Jupyter Notebook da PYNQ permite escrever código Python que interage diretamente com o hardware da FPGA, carregar overlays e controlar periféricos AXI mapeados em memória. Vale lembrar que esse código roda no processador ARM da própria placa, e não no seu computador.

6. Compartilhamento de arquivos via Samba

Além do Jupyter, a placa roda por padrão um serviço de compartilhamento de arquivos Samba. Isso permite enxergar o diretório do usuário da placa como uma unidade de rede no seu computador, o que facilita muito copiar arquivos de um lado para o outro, por exemplo os arquivos de overlay que vamos gerar no Vivado.

No Windows, abra o Explorador de Arquivos (atalho Win+E) ou a janela Executar (Win+R) e digite o endereço da placa precedido de duas barras invertidas:

1
\\192.168.2.99

Você também pode usar \\pynq caso tenha configurado o nome de host. Quando o sistema pedir as credenciais de rede, informe o usuário xilinx e a senha xilinx.

Ao entrar, aparece a pasta do usuário xilinx, que corresponde ao diretório /home/xilinx da placa. Dentro dela está a pasta jupyter_notebooks, o mesmo diretório de trabalho que aparece no Jupyter. Você pode criar pastas e copiar arquivos ali como faria com qualquer pasta local.

Em Linux, o mesmo compartilhamento pode ser acessado pelo gerenciador de arquivos usando o endereço smb://192.168.2.99.

7. Fluxo de desenvolvimento no Vivado

Esta seção descreve como criar um overlay no Vivado4 e prepará-lo para a placa. Um overlay é o projeto da lógica programável (a parte FPGA do Zynq) e normalmente é formado por dois arquivos:

  • um arquivo .bit, o bitstream que configura a FPGA;
  • um arquivo .hwh, que descreve a configuração de hardware do projeto (os IPs presentes e seus endereços) para que o PYNQ saiba como conversar com ele.

Criação do projeto

  1. Abra o Vivado e selecione Create New Project.

  2. Defina o nome e marque a opção Create project subdirectory.

  3. Selecione o tipo RTL Project e marque Do not specify sources at this time.

  4. Em Default Part, selecione o SoC correspondente à sua placa. No caso da PZ7020, baseada no Zynq-7020:

    1
    xc7z020clg484-1

    Para outra placa, basta escolher o part number do SoC dela.

  5. Clique em Finish.

Tela inicial do Vivado ao criar um novo projeto

Criação do Block Design

  1. No painel Flow Navigator, na seção IP INTEGRATOR, clique em Create Block Design.

    Flow Navigator com a opção Create Block Design em destaque

    Dê um nome ao design (por exemplo system) e confirme em OK.

    Janela Create Block Design com o campo de nome e o botão OK

  2. Com o diagrama aberto, clique no botão ”+” da barra de ferramentas para adicionar um IP.

    Botão de adicionar IP na barra do diagrama em destaque

    Pesquise por zynq e dê um duplo clique no ZYNQ7 Processing System.

    Busca por zynq mostrando o ZYNQ7 Processing System

  3. Logo após adicionar o Zynq, aparece uma faixa verde de assistência. Clique em Run Block Automation, que configura automaticamente o PS para a placa.

    Faixa verde com o link Run Block Automation em destaque

    Confirme as opções e clique em OK.

    Janela Run Block Automation com o botão OK em destaque

  4. Para habilitar os recursos que o seu overlay vai usar, como as portas AXI ou as interfaces EMIO de GPIO, dê um duplo clique no bloco do Zynq e ajuste as configurações conforme a sua aplicação.

Para casos simples, é possível usar as GPIOs EMIO do próprio PS para acionar LEDs e botões sem nenhum IP adicional. Aqui, porém, vamos seguir o caminho mais flexível, que serve para qualquer aplicação: adicionar um IP no lado da lógica programável e conectá-lo ao PS pela interface AXI.

Criação de IP personalizado com interface AXI

Para encapsular sua própria lógica VHDL/Verilog como um periférico AXI acessível pelo PS:

  1. Vá no menu Tools e escolha Create and Package New IP.

    Menu Tools com a opção Create and Package New IP em destaque

    Na tela inicial do assistente, clique em Next.

    Tela de boas-vindas do assistente com o botão Next em destaque

  2. Selecione Create a new AXI4 peripheral e clique em Next.

    Opção Create a new AXI4 peripheral selecionada

  3. Defina nome, versão e descrição do IP e clique em Next.

    Tela Peripheral Details com o campo de nome preenchido

  4. Configure a interface AXI, definindo o tipo, a largura dos dados e o número de registradores. Em seguida clique em Next.

    Tela Add Interfaces com a configuração da interface AXI

  5. Na tela de resumo, marque a opção Edit IP e clique em Finish para abrir o projeto do IP.

    Tela de resumo com a opção Edit IP marcada e o botão Finish em destaque

  6. Edite o arquivo *_S00_AXI.vhd para implementar sua lógica nos registradores AXI. O comentário no código indica os trechos onde a sua lógica de usuário deve entrar.

    Editor mostrando o arquivo _S00_AXI.vhd com a região para a lógica do usuário

  7. Concluída a edição, abra a etapa Review and Package e clique em Re-Package IP.

    Etapa Review and Package com o botão Re-Package IP em destaque

  8. De volta ao Block Design principal, clique em ”+”, procure pelo IP recém criado e adicione-o. Quando a faixa de assistência aparecer, clique em Run Connection Automation para conectar os sinais AXI ao PS automaticamente.

    IP personalizado no diagrama com o link Run Connection Automation em destaque

Geração do bitstream

Com o IP integrado ao Block Design, o fim do fluxo é:

  1. No painel Sources, clique com o botão direito sobre o block design e selecione Create HDL Wrapper, marcando a opção de atualização automática.

    Menu de contexto do block design com a opção Create HDL Wrapper em destaque

  2. No Flow Navigator, clique em Generate Bitstream e aguarde a síntese e a implementação.

    Flow Navigator com a opção Generate Bitstream em destaque

  3. Exporte o bitstream pelo menu File, na opção Export, escolhendo Export Bitstream File.

Organizando os arquivos do overlay

Aqui está um detalhe que costuma passar despercebido e impede o overlay de carregar. O PYNQ exige que o arquivo .hwh tenha exatamente o mesmo nome do arquivo .bit. Os dois arquivos ficam em locais diferentes do projeto:

ArquivoLocalização
system.bitonde você exportou o bitstream
system.hwh<proj>.srcs/sources_1/bd/system/hw_handoff/

Para deixar tudo organizado, crie uma pasta (por exemplo ready_to_test) e copie os dois arquivos para dentro dela, renomeando-os para que tenham o mesmo nome. Por exemplo, se você chamar o overlay de meu_overlay, a pasta deve conter meu_overlay.bit e meu_overlay.hwh.

8. Implantação na placa

Com a pasta do overlay pronta, copie-a para a placa. Você tem duas opções.

A primeira é usar o compartilhamento Samba que configuramos antes. Acesse \\192.168.2.99, entre em jupyter_notebooks, crie uma pasta para a sua aplicação (por exemplo app) e arraste a pasta do overlay para dentro dela.

A segunda é transferir por SCP a partir do terminal:

Terminal window
1
scp -r ready_to_test xilinx@192.168.2.99:/home/xilinx/jupyter_notebooks/app/

A senha do usuário xilinx é xilinx, padrão da imagem PYNQ.

9. Carregando o overlay no Python

No Jupyter da placa, crie um novo notebook dentro da pasta da sua aplicação e carregue o overlay com a biblioteca PYNQ. O caminho aponta para o arquivo .bit, e o PYNQ encontra o .hwh de mesmo nome automaticamente:

1
from pynq import Overlay
2
3
ol = Overlay("./ready_to_test/meu_overlay.bit")

Para confirmar que o bitstream foi realmente carregado na FPGA, use o método is_loaded, que retorna True em caso de sucesso:

1
ol.is_loaded()

Você também pode inspecionar o que o overlay oferece com a função de ajuda do Python, que lista os IPs e periféricos disponíveis:

1
help(ol)

Acessando o IP via registradores AXI

Os registradores AXI do seu IP ficam acessíveis como atributos Python, permitindo leitura e escrita diretamente do processador:

1
ip = ol.meu_ip_0 # instância do IP no design
2
ip.write(0x00, 0xABCD) # escreve no registrador offset 0x00
3
valor = ip.read(0x04) # lê do registrador offset 0x04
4
print(hex(valor))

10. Resumo do fluxo

EtapaFerramentaAção
1SD CardGravar a imagem PYNQ no cartão
2HardwareJumper em SD, conectar USB e Ethernet, ligar
3SerialAcompanhar o boot e fazer login (xilinx/xilinx)
4Host LinuxConfigurar IP estático na sub-rede 192.168.2.x
5NavegadorAcessar o Jupyter em http://192.168.2.99:9090
6SambaAcessar \\192.168.2.99 para copiar arquivos
7VivadoBlock Design com o Zynq PS e o IP AXI
8VivadoGerar o bitstream e exportar .bit e .hwh
9VivadoRenomear o .hwh para o mesmo nome do .bit
10Samba/SCPTransferir a pasta do overlay para a placa
11JupyterCarregar o overlay e interagir via Python

Footnotes

  1. PYNQ. Python Productivity for Adaptive Computing. Disponível em: https://www.pynq.io. ↩

  2. PYNQ. Python Productivity for Zynq — Documentation. Disponível em: https://pynq.readthedocs.io. ↩

  3. PYNQ. Supported Boards. Disponível em: https://www.pynq.io/boards.html. ↩

  4. AMD/Xilinx. Vivado Design Suite User Guide (UG910). Disponível em: https://docs.amd.com. ↩