Verilog → draw.io: Diagrama de Arquitetura / Diagrama Estrutural / Máquina de Estados / Estrutura de Controle
Quatro perspectivas, escolha conforme a pergunta: Qual é a ideia de projeto (para explicar a alguém, colocar em artigo/relatório) → arch2drawio.py (seção zero, padrão);
Como é a aparência no nível de portas (caminho de dados unidade por unidade) → rtl2drawio.py; Como os estados fluem (FSM individual) → fsm2drawio.py fsm;
Quem controla quem (estrutura geral de controle) → fsm2drawio.py ctrl. Módulos focados em controle (FSM, decodificação, árbitros) ficam confusos com diagramas estruturais; use as duas últimas opções.
Dependências: PyYAML; para exportar PNG/SVG é necessário drawio (ordem de busca $DRAWIO_BIN → PATH → aplicativo macOS → ~/.local/opt/drawio;
Linux sem $DISPLAY usa automaticamente xvfb-run; servidores sem root podem instalar com tools/install-drawio-linux.sh do repositório). Sem exportador, todos os comandos geram .drawio normalmente, apenas sem PNG, com uma linha de aviso no stderr. Diagramas estruturais/controle também precisam de yosys, sv2v (SystemVerilog), dot (Graphviz), e o drawio-skill instalado
(padrão ~/.claude/skills/drawio-skill, substituível pela variável de ambiente DRAWIO_SKILL_DIR; ele só chama autolayout.py, não o modifica).
Os três scripts de geração aceitam --png / --svg / --scale N (escala PNG padrão 3, aproximadamente 1 unidade de modelo = 3 px; $DRAWIO_SCALE pode alterar o padrão),
ou exporte separadamente: python3 $S/scripts/drawio_export.py X.drawio --png --svg. Para inserir em Markdown/Obsidian, prefira SVG (não desfoca).
$S é o diretório deste skill. examples/ contém dois exemplos de referência: wrr_arbiter.v (único estágio + registrador de estado), load_pipe.v (pipeline multinível com replay).
Zero: Diagrama de Arquitetura Estilo Artigo (arch2drawio.py) – Padrão
A entrada é código (ou artigo, patente, descrição do usuário). Netlists do Yosys são muito detalhadas para diagramas de arquitetura, portanto aqui não há síntese: o script fornece o esboço do código-fonte, você (agente) lê o código, captura o essencial e escreve a descrição do diagrama, e o script então gera o diagrama deterministicamente. O usuário não precisa escrever nenhum arquivo de descrição; apenas veja o diagrama, a menos que o usuário queira modificar a descrição.
python3 $S/scripts/arch2drawio.py outline src/*.sv --top gpu [--module core] # Esboço do código-fonte (sem síntese)
python3 $S/scripts/arch2drawio.py render fig.yaml -o diagrams/arch_core.drawio --png
outline lista: árvore de hierarquia (generate replicado ×N), portas, arrays de memória (profundidade × largura, campos de packed struct, arrays de mesma profundidade sugerem "possivelmente mesma tabela"),
registradores sequenciais nomeados por estágio (s1_ / stage2_ / if_id_ …), máquinas de estado, sinais suspeitos de realimentação (replay/kill/flush/stall/forward/refill…),
fluxo de dados entre instâncias (via sinais compartilhados, já excluindo clk/reset/variáveis de loop). Para entrada de artigo/patente/descrição, esta etapa é pulada.
Regras de desenho (ao escrever a descrição, siga)
- Desenhe apenas o essencial: Primeiro, explique a ideia central do design em uma ou duas frases; mantenha apenas os componentes que a materializam; lógica de cola, clk/reset, portas de depuração e controles dispersos são omitidos. Geralmente 8–20 caixas; se exceder, divida em duas (uma visão geral + uma de unidade chave).
- Destaque os pontos principais: 1–3 pontos de design chave com
hl: true(cor contrastante: fundo escuro, texto branco) oubold: true(negrito); linhas de caminho crítico também podem ter| hl. Para enfatizar uma área, usegroups+hl: true(caixa de fundo claro). - Não numere cada componente: Numere apenas os componentes centrais com
num: Nquando necessário para referência textual, e explique emcallouts. - Texto conciso nas caixas: Dentro da caixa, apenas o nome (≤ 2 linhas); o significado é expresso pela forma: operações usam
adder/mult/cmp/op(símbolo circular) oualu, seleção usamux(pino de seleçãox.selentra por baixo), armazenamento usatable, fila usafifo, registrador usareg, porta usaand/or/xor/not. - Tabelas SRAM mostram campos de uma única entrada: Os
fieldsdetableescrevem o significado de cada campo (ex.:[V, Tag (20b), Data (32b)]), para conjunto associativo múltiplo, usestack: 4(empilhado + ×4); arrays paralelos de mesma profundidade e endereço são combinados em uma tabela, cada array como um campo. - Pipeline:
stagesdefine os estágios (id+ funçãosub, ex.:{id: S1, sub: Verificação de hit}), o script desenha divisórias tracejadas e nomes dos estágios; registradores/FIFOs entre estágios usambetween: [S0, S1], o script os coloca na linha divisória; realimentação (destino em estágio anterior) é automaticamente roteada pelo corredor inferior, ou pode ser| fb,| fb via=top(rota por cima). Múltiplos pipelines usamlanes, cada lane compartilha as colunas dos estágios, e estágios temporalmente correspondentes são automaticamente alinhados. - Diagramas de nível de bloco sem estágios (topo, SoC) não escrevem
stages; unidades replicadas usamstack: N; recursos por thread/por núcleo são agrupados comgroups.
Formato da descrição (YAML)
title: dcache_pipe: Pipeline de busca dcache de mapeamento direto
stages: [{id: S0, sub: Seleção / Leitura de array}, {id: S1, sub: Verificação de hit}, {id: S2, sub: Miss / Repetição}]
lanes: [{id: ld, name: Pipeline Load}, {id: st, name: Pipeline Store}] # Opcional, para múltiplos pipelines
nodes:
- {id: arr, type: table, label: "Cache Array · 64 entradas", fields: [V, "Tag (20b)", "Data (32b)"], stage: S0}
- {id: r1, type: reg, label: addr, between: [S0, S1]} # Na divisória S0|S1
- {id: tcmp, type: cmp, label: Comparação de Tag, stage: S1, hl: true} # Círculo "=", texto explicativo abaixo
- {id: rmux, type: mux, stage: S0, ports: 2}
edges:
- "rmux => arr : idx | w=6" # -> dados => barramento (grosso) ~> controle (tracejado); w=N largura
- "rpl -> rmux.sel : replay | fb hl" # .sel/.en entra por baixo, .top por cima; fb realimentação, hl destaque, via=top, pos=src|dst
groups: [{label: "Por thread ×4", members: [rf, alu, lsu], hl: true}]
legend: {reg: Registrador de pipeline, fb: Realimentação, ctrl: Controle, hl: Design chave} # Chaves disponíveis: data bus ctrl fb hl hl_edge hl_region reg
callouts: {1: "Numere apenas para referência"}
Tipos de nó: block reg fifo table(fields, rows, stack) array(dims, cell) mux(ports) adder mult cmp/op(sym)
alu and or xor nand nor xnor not cloud mem xbar in out text dot.
Atributos gerais: stage / lane / between, hl bold num stack, ajuste fino com row / col (linha/coluna dentro do estágio, pode ser negativo), dx / dy, w / h.
Outros: array suporta cell_labels (rótulos por célula, ex.: T0..T3) e cell_h (retângulos altos, para canais de thread); groups suporta stack: N
(empilha área inteira, ex.: "Core ×2" e mostra interior) e label_align: right; name de lanes vazio = apenas divisão sem rótulo;
arestas com | both desenham seta dupla (requisição/resposta); realimentação entre duas unidades na mesma coluna vira uma linha vertical curta e rente (ex.: Dispatcher ⇅ Core).
Fluxo
outline+ leitura do código-fonte chave (ou leitura do artigo/patente), anote a ideia central → decida qual camada desenhar e quais são os pontos principais.- Escreva a descrição em um diretório temporário (scratchpad), cada nó recebe
stage(quando houver estágios). Os loops são quebrados automaticamente: nós com nomes de controlador (sched / ctrl / fsm / arbiter / dispatch / 调度 / 控制 …, seguido por grande fan-out de linhas de controle) são colocados primeiro, suas saídas vão para a direita e arestas que voltam para eles são marcadas como realimentação; se a classificação estiver errada, escreva explicitamente| fbna aresta. Opcional: Referência de composição (gpt-image-bridge, requer aprovação do usuário para cada imagem) – O posicionador de grade garante conexões corretas, mas não "compõe". Quando o módulo tem ≥ 10 ou há partições óbvias (plano de controle/plano de dados/hierarquia de memória), pode-se gerar uma linha de base no draw.io e, em seguida, escrever a lista de módulos e conexões obtida do código no prompt (estilo de ilustração de artigo, fundo branco, uma única cor de destaque, layout de três regiões, texto mínimo, rótulos curtos em inglês), gerar 1 esboço com gpt-image-bridge e lê-lo. Apenas a composição e hierarquia visual são usadas (partições, quem está acima de quem, formação, espaços em branco, posição dos itens importantes); converta paralane/row/col/groups; Módulos, conexões e nomes sempre se baseiam no código – Erros comuns em esboços: conectar fonte errada, módulos a mais/menos, fundo escuro. Antes de gerar, liste as imagens (uso, caminho, dimensão, resumo do prompt) conforme as regras deste skill e aguarde consentimento explícito do usuário; esboços salvos emdiagrams/sketch_*.png, cabeçalho do arquivo de descrição referencia. render --png→validate.py(erro deve ser 0; avisos de overlap/through-vertex nas divisórias tracejadas que cruzam cabeçalhos de estágio/registradores são intencionais) → Leia o PNG para inspeção visual, ajuste fino comrow/colno máximo duas rodadas (comum: fazer o caminho de dados principal ocupar a linha 0 em linha reta, caminhos alternativos/controle na linha seguinte).- Entregue
.drawio+.png; o arquivo de descrição permanece no scratchpad; entregue ao usuário apenas se ele quiser modificar.
Exemplos: topo do tiny-gpu ~/hardware-project/tiny-gpu/diagrams/arch_gpu.* (composição guiada pelo esboço sketch_gpu_top.png,
compare com arch_gpu_baseline.png sem esboço); examples/dcache_pipe.v → dcache_pipe.arch.yaml (descrição escrita a partir do código) → .png; arch_dual_issue.yaml (alinhamento de pipeline duplo + encaminhamento +
notas numeradas); arch_mac.yaml (nível de circuito: símbolos de multiplicador/somador, realimentação de acumulação, pino de seleção MUX). Diagrama de bloco do core tiny-gpu em
~/hardware-project/tiny-gpu/diagrams/arch_core.*.
Um: Diagrama Estrutural (rtl2drawio.py, Detalhes de Portas)
python3 $S/scripts/rtl2drawio.py src/*.sv --top gpu --list # Módulos e composição de unidades
python3 $S/scripts/rtl2drawio.py src/*.sv --top gpu --module alu -o diagrams/alu.drawio --png
Fluxo: sv2v (automático para .sv) → Yosys proc; opt; opt_dff; wreduce; memory -nomap → netlist JSON → análise de pipeline por unidade → simplificação → layout com restrição de coluna.
Após a geração, execute python3 ~/.claude/skills/drawio-skill/scripts/validate.py X.drawio (erro deve ser 0; avisos de through-vertex/overlap devido a linhas de divisória de estágio/registradores sobre a linha divisória
são intencionais), então leia o PNG para inspeção visual.
Regras de layout (--layout pipeline; padrão auto: módulos hierárquicos com predominância de instâncias de submódulos usam layout livre, os demais usam layout por estágio de pipeline)
- Entrada à esquerda, saída à direita, lógica organizada da esquerda para a direita por profundidade combinacional; pipelines multinível vão de S0 → Sn da esquerda para a direita, divisórias tracejadas, cabeçalho de coluna com número do estágio.
- Registradores divididos em duas classes:
- Registradores de pipeline (faixa vertical estreita) colocados nas linhas divisórias de estágio; a fileira final que só se conecta a portas de saída são registradores de saída (não contam como um estágio, não desenham divisória).
- Registradores de estado (FSM/contadores/pontos: saída volta à própria entrada apenas por lógica combinacional) desenhados dentro do estágio, suas saídas como realimentação.
- Quando o nome do registrador inclui número de estágio (
s2_x,x_s2,stage2_x,if_id_/id_ex_/ex_mem_/mem_wb_, e pelo menos dois tipos) o nome prevalece – bits de valid com kill, mesmo com loop combinacional, ainda são registradores de pipeline. Designs sem nomes baseados em estágio só podem ser inferidos por estrutura; o tempo de chegada das portas de entrada é desconhecido, o número do estágio pode ser precoce.
- Cálculo do estágio: A partir de registradores com número de estágio, o estágio de uma unidade = o registrando operando mais tardio (máximo); lógica acionada apenas por portas de entrada é classificada pelo estágio do registrador que a "amostra" (ALAP), as portas de entrada correspondentes também são colocadas no início desse estágio (ex.: resposta TLB colocada em S1).
- Realimentação: Sinais que entram no cone de entrada de um registrador com número de estágio, mas só podem vir de estágios posteriores (replay→S0, kill→S1), e loops de registradores de estado, são desenhados como
linhas vermelhas contornando por baixo, com maior alcance por fora; se houver mais de 3 realimentações entre o mesmo par de estágios, desenhe apenas uma linha grossa representando um feixe (rótulo
Realimentação ×N: …). - Agrupamento: Se houver mais de 4 registradores de pipeline na mesma divisória, combine em um grupo de registradores (faixa vertical com rótulo
Registradores S1→S2 ×Ne largura total). - Simplificação: Registradores com mesmo nome e fatias são mesclados; cadeias de MUX de if/case aninhados são fundidas em um único MUX
N:1; pequena lógica de decodificação que aciona apenas um pino de seleção MUX / enable de registrador é incorporada como linha de seleção / linha de enable; até 3 portas são desenhadas como símbolos de porta AND/OR/NOT/XOR, portas maiores são dobradas em nuvem "lógica combinacional" (cortadas em torno de unidades de operação para evitar loops falsos); unidades de mesma origem (mesma localização no código-fonte) expandidas por generate e instânciasthreads[i].xsão dobradas em "×N" (com sombra). Todas as mesclagens são feitas apenas dentro do mesmo estágio. clk/reset ocultos por padrão. - Conexões: linha sólida = dados (grossa = barramento múltiplo,
/N= largura); linha roxa tracejada = seleção MUX; linha azul pontilhada = enable; linha vermelha = realimentação.
Parâmetros
| Parâmetro | Função |
|---|---|
--list | Lista módulos e composição de unidades e sai |
--module M | Desenha qual módulo (variante parametrizada desenha a primeira, com aviso) |
--layout auto|pipeline|free | Padrão auto: módulos com instâncias de submódulos e poucos nós de lógica combinacional (módulos de cola de topo) usam free (comunicação bidirecional entre blocos não é realimentação de pipeline), caso contrário pipeline |
--show-clock | Desenha clk/reset |
--no-fold | Não dobra instâncias generate/unidades de mesma origem |
--no-collapse | Não dobra lógica de controle/combinacional |
--sv2v auto|always|never | Frontend SV (sintaxe como portas de array não compactado requer sv2v) |
--keep-json PATH | Mantém netlist JSON do Yosys |
Dois: Diagrama de Máquina de Estados (fsm2drawio.py fsm)
python3 $S/scripts/fsm2drawio.py list src/*.sv
python3 $S/scripts/fsm2drawio.py fsm src/*.sv [--module M] -d diagrams --png [--no-actions]
- Analisa diretamente o código-fonte, as condições de transição mantêm a expressão original. Reconhece atribuições a S (ou variável de próximo estado
S <= N) em ramoscase (S); os nomes de estado são obtidos de localparam; constantes de variáveis de estado de outras FSMs são traduzidas para o nome do estado da outra FSM (core_state == 3'b001→core_state == FETCH). - Condição de transição = if/case/for que envolve diretamente a atribuição de estado;
else ifescreve apenas sua própria condição; condições emforsão escritas como∃j: …;for iexterno envolvendo todo o case indica replicação por i; guardas fora do case (enable, ramo de leitura/escrita) entram em caixa de comentário; variáveis booleanas locais setadas em loop têm definição na caixa de comentário. - Desenho: Cada estado é um círculo de tamanho igual (nome quebra em
_, abaixo em letras pequenas a codificação e ⟳), estado final círculo duplo, ponto preto = entrada de reset. Quando o fluxo principal cobre a maioria dos estados, organize em loop em forma de U (linha superior esquerda→direita, desce, linha inferior direita→esquerda); ramificações organizadas por distância do estado inicialem colunas uniformes. Estados adjacentes conectados diretamente; transições que pulam outros estadosarqueiam por cima/baixo, rótulo no ápice; A↔B bidirecional cada um curvado para um lado; self-loop explícito desenhado acima do círculo. Espaçamento de coluna adapta-se ao rótulo mais longo. Após/, atribuições na transição (no máximo 2 exibidas). - Não reconhece: Estruturas sem
case(state)(contadores, decodificação puramente combinacional) – não aparecem emlist, conforme esperado.
Três: Estrutura Geral de Controle (fsm2drawio.py ctrl)
python3 $S/scripts/fsm2drawio.py ctrl src/*.sv --top gpu --module core -o diagrams/ctrl_core.drawio --png
- Desenha instâncias de submódulos do módulo L (instâncias generate dobradas ×N), mantêm apenas sinais de controle: linha laranja grossa = broadcast de estado FSM; linha azul = handshake valid/ready; linha roxa tracejada = start/done/enable/seleção/reset etc.; barramentos de dados omitidos. Caixas de instância com FSM listam sequência de estados, submódulos com FSM são marcados "Contém FSM".
- Sinais de controle penetram no máximo 3 níveis de unidades básicas (comparador, porta, registrador) para rastrear a origem. Classificação baseada em nome de sinal; nomes não padronizados podem resultar em omissão/excesso.
- Sugestão de cima para baixo: primeiro topo (quem inicia quem, quem aperta a mão com a memória) → próxima camada (como a FSM mestre escala) → diagrama de estado da FSM chave.
Limitações
- O layout é feito automaticamente pelo Graphviz com restrições de coluna; cruzamentos não podem ser completamente eliminados; ajuste fino no draw.io arrastando (todos os objetos são nativamente editáveis).
- Módulos de controle grandes (centenas de unidades) ainda produzirão diagramas estruturais confusos; conforme acima, mude para diagrama de máquina de estados / estrutura de controle, ou desenhe submódulo por submódulo.
- Círculos/arcos de diagrama de estado são desenhados diretamente por coordenadas; ao arrastar um estado, os pontos de inflexão dos arcos não acompanham, precisam ser ajustados manualmente.