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:
- Preparação do cartão SD e inicialização do sistema;
- Acesso ao terminal pela porta serial;
- Configuração de rede para acesso via Ethernet;
- Acesso à interface Jupyter Notebook;
- Compartilhamento de arquivos via Samba;
- 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.
- Baixe a imagem
.imgdo sistema PYNQ correspondente à sua placa. - 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
ddou também o balenaEtcher.
- 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:
1sudo apt install minicomConexão serial
Com o cabo USB já conectado ao conector JTAG/UART da placa, abra o terminal serial:
1sudo minicom -D /dev/ttyUSB0 -b 115200Os parâmetros de comunicação são:
| Parâmetro | Valor |
|---|---|
| Taxa de baud | 115200 |
| Bits de dados | 8 |
| Paridade | nenhuma |
| Bits de parada | 1 |
| Controle de fluxo | nenhum |
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:
1ip aProcure pela interface ligada ao cabo (por exemplo enp4s0 ou eth0). Em
seguida, atribua o endereço IP:
1sudo ip addr add 192.168.2.1/24 dev enp4s02sudo ip link set enp4s0 upConfiguraçã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:
1sudo nano /etc/netplan/01-netcfg.yamlAdicione o seguinte conteúdo, ajustando o nome da interface conforme necessário:
1network:2 version: 23 ethernets:4 enp4s0:5 addresses:6 - 192.168.2.1/247 dhcp4: falsePor segurança, o Netplan exige que o arquivo seja legível apenas pelo root:
1sudo chmod 600 /etc/netplan/01-netcfg.yamlAplique as configurações:
1sudo netplan applyConfiguraçã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:
1sudo nano /etc/hostsAdicione a seguinte linha ao final do arquivo:
1192.168.2.99 pynq.local pynqApós salvar, a placa estará acessível pelos seguintes endereços:
| Endereço | Descrição |
|---|---|
http://192.168.2.99:9090 | Endereço IP direto |
http://pynq.local:9090 | Nome de host local |
http://pynq:9090 | Alias curto |
5. Acesso ao Jupyter Notebook
Com a rede configurada, abra um navegador e acesse o endereço da placa na porta 9090:
1http://192.168.2.99:9090Se 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.99Você 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
-
Abra o Vivado e selecione Create New Project.
-
Defina o nome e marque a opção Create project subdirectory.
-
Selecione o tipo RTL Project e marque Do not specify sources at this time.
-
Em Default Part, selecione o SoC correspondente à sua placa. No caso da PZ7020, baseada no Zynq-7020:
1xc7z020clg484-1Para outra placa, basta escolher o part number do SoC dela.
-
Clique em Finish.

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

Dê um nome ao design (por exemplo
system) e confirme em OK.
-
Com o diagrama aberto, clique no botão ”+” da barra de ferramentas para adicionar um IP.

Pesquise por
zynqe dê um duplo clique no ZYNQ7 Processing System.
-
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.

Confirme as opções e clique em OK.

-
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:
-
Vá no menu Tools e escolha Create and Package New IP.

Na tela inicial do assistente, clique em Next.

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

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

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

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

-
Edite o arquivo
*_S00_AXI.vhdpara 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.
-
Concluída a edição, abra a etapa Review and Package e clique em Re-Package IP.

-
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.

Geração do bitstream
Com o IP integrado ao Block Design, o fim do fluxo é:
-
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.

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

-
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:
| Arquivo | Localização |
|---|---|
system.bit | onde 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:
1scp -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:
1from pynq import Overlay2
3ol = 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:
1ol.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:
1help(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:
1ip = ol.meu_ip_0 # instância do IP no design2ip.write(0x00, 0xABCD) # escreve no registrador offset 0x003valor = ip.read(0x04) # lê do registrador offset 0x044print(hex(valor))10. Resumo do fluxo
| Etapa | Ferramenta | Ação |
|---|---|---|
| 1 | SD Card | Gravar a imagem PYNQ no cartão |
| 2 | Hardware | Jumper em SD, conectar USB e Ethernet, ligar |
| 3 | Serial | Acompanhar o boot e fazer login (xilinx/xilinx) |
| 4 | Host Linux | Configurar IP estático na sub-rede 192.168.2.x |
| 5 | Navegador | Acessar o Jupyter em http://192.168.2.99:9090 |
| 6 | Samba | Acessar \\192.168.2.99 para copiar arquivos |
| 7 | Vivado | Block Design com o Zynq PS e o IP AXI |
| 8 | Vivado | Gerar o bitstream e exportar .bit e .hwh |
| 9 | Vivado | Renomear o .hwh para o mesmo nome do .bit |
| 10 | Samba/SCP | Transferir a pasta do overlay para a placa |
| 11 | Jupyter | Carregar o overlay e interagir via Python |
Footnotes
-
PYNQ. Python Productivity for Adaptive Computing. Disponível em: https://www.pynq.io. ↩
-
PYNQ. Python Productivity for Zynq — Documentation. Disponível em: https://pynq.readthedocs.io. ↩
-
PYNQ. Supported Boards. Disponível em: https://www.pynq.io/boards.html. ↩
-
AMD/Xilinx. Vivado Design Suite User Guide (UG910). Disponível em: https://docs.amd.com. ↩