Figuras de hardware TikZ (hwfig v1.1)
Use esta habilidade sempre que o usuário quiser uma figura de hardware em LaTeX/TikZ: dataflow de um kernel HLS/FPGA, uma camada de NN quantizada, um diagrama de bloco SoC Versal/Zynq, matrizes AI Engine, matrizes sistólicas, árvores de adição, pipelines, NoCs, hierarquias de memória, domínios de clock, floorplans. A saída é sempre código TikZ editável mais uma visualização PDF/PNG compilada.
Fluxo de trabalho
- Obter o pacote. Copie
hwfig.stydo diretório base desta habilidade para o diretório de trabalho (a menos que o usuário já tenha uma cópia — se a cópia dele não tiverv1.1na linha\ProvidesPackage, ofereça para atualizá-la). Leia o arquivo e copie-o byte por byte; nunca redigite ou redefina os estilos inline — as paletas só funcionam através das cores de papéis. - Escolher uma paleta (padrão
grey), ou como opção de pacote\usepackage[okabe]{hwfig}ou com\hwpalette{...}em qualquer lugar (local ao grupo, portanto por figura):grey— padrão para impressão/periódicos, espelha o modelo de referência (caixas de computação brancas, quantizadores cinza, constantes cinza tracejadas).okabe— Okabe–Ito, seguro para daltônicos, contraste mais forte. Boa opção padrão para artigos coloridos.tol— Paul Tol muted, seguro para daltônicos, mais suave.nord— tons frios, para slides.slate— azul-cinza + malva + coral, combina com decks de slides científicos. Se o usuário não especificar, produzagreye ofereça uma paleta colorida; para comparar, compile a mesma figura em várias paletas (loop\foreach \pal in {grey,okabe,...}{\hwpalette{\pal}...}dentro de um documentostandalonedá uma página para cada). Um nome de paleta desconhecido é um erro grave.
- Esboçar o layout em uma grade primeiro (coordenadas explícitas
at (x,y), espaçamento de colunas ~2.4 cm, linhas ±1.4 cm), depois conectar. Coordenadas explícitas vencempositioningpara figuras densas de dataflow. - Compilar como
\documentclass[tikz,border=4pt]{standalone}+\usepackage{hwfig}compdflatex -interaction=batchmode -halt-on-error(com time-out;nonstopmodepode travar), rasterizar compdftoppm -r 110 -png, olhar o PNG, e corrigir sobreposições (rótulos em fios, fios cruzando caixas, títulos colidindo, rótulos de quadro tocando nós) antes de entregar. Iterar até ficar limpo. - Entregar o
.tex(corpo da figura pronto para colar em um artigo), o.sty, e o PDF. Lembre o usuário de que no artigo dele precisa de\usepackage{hwfig}(ou\usepackage[<palette>]{hwfig}).
Gramática visual (mantenha consistente)
- Dados fluem esquerda → direita; o caminho principal fica em um eixo horizontal, ramos paralelos acima/abaixo (±1.4 cm).
- Constantes de compilação (
ctime, caixa tracejada) ficam diretamente acima/abaixo do bloco que configuram e conectam comdarr(seta tracejada). - Todo bloco de computação que muda de precisão é seguido por um
qbox(requant/deslocamento/saturação). - Ramos convergem em um nó
opcom fios que se aproximam verticalmente:\draw[arr] (qr.east) -| ($(sum.north)+(0,0.32)$) -- (sum.north); - Ramos alternativos de uma fonte: uma coordenada de bifurcação com um ponto,
\fill[hwwire] (fork) circle (1.3pt);, depois cotovelos|-para cada ramo. - Use
hotpara o caminho crítico/destacado,buspara datapaths largos compic[pos=.5]{buswidth=512},node[lbl,midway,above]{AXI4-S}para nomes de protocolo (lblboxquando o rótulo precisa ficar sobre fios que cruzam). - Molduras de agrupamento vão na camada de fundo com
fit:\begin{scope}[on background layer]\node[group,fit=(a)(b),label={[grouplabel]above:PL}] {};\end{scope} - Domínios de clock:
clkdomain(cor de destaque, tracejado) ouclkdomain=<color>; marque cruzamentos com um nócdc. - Comece toda figura com
\begin{tikzpicture}[hw](define fonte\footnotesize, largura de linha, pontas de seta).
Roteamento e rótulos (verifique em cada PNG)
- Lacunas entre regiões. Deixe ≥ 0.6 cm entre quadros adjacentes (Runtime | Passagem de mensagens, PL | AIE). Nós, operações
opde junção e fios nunca devem ficar sobre a borda de um quadro — se um nó não pertence a nenhum dos quadros, dê espaço fora de ambos. - Canais de roteamento. Fios entre regiões correm em um canal vazio entre os quadros (defina:
\def\chan{6.7}), depois em seu próprio nível horizontal abaixo/acima do quadro de destino (\def\lvla{-1.7}). Dois fios nunca compartilham um nível; mantenha ≥ 0.3 cm entre um fio e qualquer borda de quadro ou lado de bloco paralelo, e ≥ 0.4 cm entre fios paralelos. - Rótulos ficam apenas em segmentos livres. Coloque
lbl/buswidthem um segmento que esteja inteiramente em espaço vazio — nunca onde o fio cruza uma borda de quadro, e nunca a menos de ~0.2 cm de um bloco. Se a única extensão livre é vertical, gire o rótulo para ler de baixo para cima:node[lbl,rotate=90,below]{AXI4-S}. Em links curtos usepic[pos=.35..0.4]para que o número fique longe do bloco de destino. - Segmentos explícitos para cotovelos com rótulos.
-|/|-não podem receber um rótulo em uma perna escolhida; escreva os cantos explicitamente:(a.east) -- (\chan,ya) -- node[lbl,...]{X} (\chan,\lvla) -- (b.south|-{(0,\lvla)}) -- (b.south); - Matemática em rótulos:
$W_{\mathrm{root}}$; rótulos de duas linhas com\\(todos os estilos definemalign=center).
Estilos de nó (todos sensíveis à paleta)
| estilo | uso |
|---|---|
runtime | entradas/saídas de tempo de execução, portas, tensores |
ctime | constantes de compilação: pesos, escalas, config |
hlsbox | kernel de computação / função HLS / kernel AIE |
qbox | quantizador, requant, deslocamento+sat |
op | +, ×, max (círculo) |
mux, demux | multiplexador (entradas oeste, saída leste) / demultiplexador (espelhado) |
mem, bram, uram | buffers on-chip |
fifo | stream/FIFO |
reg / ff | registrador com cunha de clock |
lut, dsp | primitivas FPGA (dsp coloca uma pequena etiqueta "DSP" fora do canto superior direito — deixe espaço) |
extmem | DDR / HBM (cilindro) |
cpu | PS / ARM / RISC-V (moldura dupla) |
host | CPU host (lado PCIe) |
xbar | interconexão AXI / barra NoC (\rotatebox{90}{AXI IC}) |
router | roteador/switch NoC (losango) |
pe | elemento de processamento |
aie | tile AI Engine (núcleo + memória local; rótulo centralizado no núcleo) |
chip | encapsulamento/die com pinos |
port | marcador de pino pequeno |
cdc | sincronizador de cruzamento de domínio de clock |
group, plregion, psregion, clkdomain | molduras de fundo |
grouplabel, lbl, lblbox | títulos de quadro, rótulos de fio (lblbox = fundo branco) |
Estilos de aresta: arr, darr, bus, hot. Marca de barramento: pic[pos=.5]{buswidth=32} (número à direita da marca) ou buswidth'=32 (à esquerda).
Pics paramétricos
Passe opções por pic — elas permanecem locais àquele pic, então tamanhos diferentes podem compartilhar uma figura:
\pic (A) at (0,0) {aiearray={rows=2,cols=4}};. \hwset{key=value,...} define padrões para o resto do escopo atual. Nós internos são endereçáveis como (name-...).
| pic | chaves | partes endereçáveis |
|---|---|---|
aiearray | rows, cols, dx, dy, tilelabel | tiles (n-t<r>-<c>) (r=1 inferior), shims (n-s<c>), moldura (n-box) |
systolic | rows, cols | (n-pe<r>-<c>) (r=1 superior) |
addertree | depth (entradas 2^depth) | entradas (n-in<k>), somadores (n-a<level>-<k>), saída (n-out) |
pipeline | labels={Load,MAC,Requant,...} | estágios (n-st<k>), regs (n-r<k>) |
noc | rows, cols, nocpe (verdadeiro/falso) | roteadores (n-n<r>-<c>), PEs (n-pe<r>-<c>) |
fabric | columns={I,C,C,D,C,B,...}, fabricrows (qualquer valor) | topos de coluna (n-c<k>). C=CLB D=DSP B=BRAM U=URAM I=IO |
legend | legend={comp/Compute,mem/BRAM,...} (papéis: io comp quant const op mem ctrl group); espaçamento automático | — |
Roteie fios ao redor de pics (por exemplo, para tiles shim por baixo: (k1.east) -- ++(0.9,0) |- ($(arr-s1.south)+(0,-0.35)$) -- (arr-s1.south)), nunca através de tiles.
Paletas personalizadas
Não são necessários internos @ — defina todos os 8 papéis (traçado, preenchimento) e as três cores de fio:
\hwdefinepalette{mine}{%
\hwrole{io}{black}{white} \hwrole{comp}{teal}{teal!12} \hwrole{quant}{orange}{orange!15}
\hwrole{const}{black!55}{black!4} \hwrole{op}{red!70!black}{red!10} \hwrole{mem}{blue!60!black}{blue!10}
\hwrole{ctrl}{violet}{violet!10} \hwrole{group}{teal!40}{teal!3}
\hwwires{black}{black!50}{red!70!black}} % fio, fio de constante, destaque/quente
\hwpalette{mine}
Mantenha preenchimentos ≤ 25% de matiz para que o texto preto permaneça legível, e verifique se a conversão para escala de cinza ainda separa os papéis.
Armadilhas
- Escreva arquivos
.texcom a ferramenta de escrita de arquivos, não com heredocs de shell,sedou literais de string Python: estes rotineiramente transformam\\em\(ou\c...em caracteres de controle) e quebram quebras de linha em texto de nó. - Dois-pontos ou vírgulas dentro do texto
label=quebram a análise de chaves — coloque entre chaves:label={[grouplabel]above:{INT8: shifts}}. - Uma lista
fit=não pode conter($(a)+(1,2)$)(a vírgula divide a lista); defina uma\coordinatenomeada primeiro. - Para uma linha extra dentro de um nó, use um nó simples com
align=centere\\[-2pt]{\tiny ...}— não sobreponha um segundo rótulo na caixa. shapes.geometrictrapeziumcom texto vazio divide por zero — é por isso quemux/demuxsão path pictures personalizadas; não as troque de volta.- Dentro de
\foreach, use\pgfmathtruncatemacropara aritmética de inteiros antes de usar valores em nomes de nós; em\foreach \a/\b in {...}uma/dentro do texto do rótulo o divide (escrevaPort, nãoI/O). clkdomaintem um preenchimento claro; passefill=noneao desenhá-lo sobre umfabricou outro conteúdo denso.- MiKTeX ignora
TEXINPUTSno estilo POSIX; mantenhahwfig.stypróximo ao.tex(ou passe--include-directory=<caminho windows>). - Mantenha figuras com ≤ ~16 cm de largura para um artigo de duas colunas em
\footnotesize; escale com\resizebox{\linewidth}{!}{...}apenas como último recurso.
Exemplo de referência (camada GNN, o modelo base)
\begin{tikzpicture}[hw]
\node[runtime] (xin) at (-0.9,1.4) {$X_i$};
\node[runtime] (xein) at (-0.9,-1.4) {$(X,E)$};
\node[hlsbox] (root) at (2.5,1.4) {Root\\$W_{\mathrm{root}}x_i$};
\node[qbox] (qr) at (4.9,1.4) {$Q_r$\\shift + sat8};
\node[mem] (agg) at (2.5,-1.4) {Mean aggregation\\$m_i$};
\node[hlsbox] (neigh) at (5.5,-1.4) {Neighbor\\$W_{\mathrm{neigh}}m_i+b$};
\node[qbox] (qn) at (8.1,-1.4) {$Q_n$\\shift + sat8};
\node[op] (sum) at (9.8,0) {$+$};
\node[qbox,minimum width=1.45cm] (qc) at (11.6,0) {sat8};
\node[hlsbox,minimum width=1.55cm] (relu) at (13.6,0) {$\rho_l$\\ReLU / id.};
\node[runtime] (yout) at (15.5,0) {$y_i^{(l)}$};
\node[ctime] (wroot) at (2.5,2.45) {$W_{\mathrm{root}}$};
\node[ctime] (wneigh) at (5.5,-2.55) {$W_{\mathrm{neigh}},b$};
\node[ctime] (sr) at (4.9,2.45) {$S_r$};
\node[ctime] (sn) at (8.1,-2.55) {$S_n$};
\node[ctrl] (fsm) at (13.6,-1.6) {Layer FSM};
\draw[arr] (xin)--(root);
\draw[bus] (xein)--(agg) pic[pos=.5]{buswidth=16};
\draw[arr] (agg)--(neigh);
\draw[arr] (root)--(qr);
\draw[arr] (neigh)--(qn);
\draw[arr] (qr.east) -| ($(sum.north)+(0,0.32)$) -- (sum.north);
\draw[arr] (qn.east) -| ($(sum.south)+(0,-0.32)$) -- (sum.south);
\draw[hot] (sum)--(qc);
\draw[hot] (qc)--(relu);
\draw[hot] (relu)--(yout);
\draw[darr] (wroot)--(root);
\draw[darr] (wneigh)--(neigh);
\draw[darr] (sr)--(qr);
\draw[darr] (sn)--(qn);
\draw[darr] (fsm)--(relu);
\begin{scope}[on background layer]
\node[group,fit=(xin)(xein),label={[grouplabel]above:Runtime}] {};
\node[group,fit=(root)(qr)(agg)(neigh)(qn)(wroot)(wneigh),label={[grouplabel]above:Message passing}] {};
\end{scope}
\end{tikzpicture}
Exemplo de referência (SoC Versal)
\begin{tikzpicture}[hw]
\node[host] (host) at (-3.4,0.3) {Host\\(x86)};
\node[cpu] (ps) at (0,2.2) {PS\\Cortex-A72};
\node[extmem] (ddr) at (0,-2.0) {DDR4};
\node[xbar,minimum height=5.6cm] (noc) at (2,0.1) {\rotatebox{90}{NoC}};
\node[hlsbox] (k1) at (4.7,1.6) {mm2s\\(HLS)};
\node[hlsbox] (k2) at (4.7,-2.3) {s2mm\\(HLS)};
\node[bram] (buf) at (4.7,-0.35) {BRAM buf};
\pic (arr) at (8.3,0.1) {aiearray};
% níveis de roteamento
\def\chan{6.7} % canal vertical entre regiões PL e AIE
\def\lvla{-1.7} % mm2s -> AIE, abaixo da moldura AIE
\def\lvlb{-2.3} % AIE -> s2mm, um nível abaixo (= linha s2mm)
\draw[bus] (host) -- (host-|noc.west) node[lbl,midway,above=2pt]{PCIe Gen4};
\draw[arr] (ps.east) -- (ps-|noc.west);
\draw[arr,<->] (ddr.east) -- (ddr-|noc.west);
\draw[bus] (k1-|noc.east) -- (k1) pic[pos=.4]{buswidth=128};
\draw[bus] (k2) -- (k2-|noc.east);
% mm2s -> shim 1: à direita no canal, desce, ao longo do nível, sobe para o shim
\draw[arr] (k1.east) -- (\chan,1.6)
-- node[lbl,rotate=90,below]{AXI4-S} (\chan,\lvla)
-- (arr-s1.south|-{(0,\lvla)}) -- (arr-s1.south);
% shim 4 -> s2mm: desce até o nível b, esquerda para s2mm
\draw[arr] (arr-s4.south) -- (arr-s4.south|-{(0,\lvlb)})
-- node[lbl,above,pos=0.2]{AXI4-S} (k2.east);
\draw[darr] (buf) -- (k1);
\draw[darr] (buf) -- (k2);
\begin{scope}[on background layer]
\node[psregion,fit=(ps),label={[grouplabel]above:PS}] {};
\node[plregion,fit=(k1)(k2)(buf),inner sep=8pt,label={[grouplabel]above:PL @ 300 MHz}] {};
\node[clkdomain,fit=(arr-box),inner sep=3pt,label={[grouplabel,text=hwaccent]above:AIE array $3\times4$ @ 1.25 GHz}] {};
\end{scope}
\end{tikzpicture}
Código-fonte do pacote
hwfig.sty (v1.1, por Pelayo Leguina, [email protected], LPPL 1.3c) acompanha este arquivo. O changelog, galeria e exemplos vivem no repositório público hwfig; esta habilidade é mantida lá em skill/ e empacotada com skill/package.sh.