TikZ-Hardwarefiguren (hwfig v1.1)
Verwenden Sie dieses Skill, wenn der Benutzer eine Hardwarefigur in LaTeX/TikZ wünscht: Datenfluss eines HLS/FPGA-Kerns, einer quantisierten NN-Schicht, eines Versal/Zynq-SoC-Blockschaltbilds, AI-Engine-Arrays, systolischer Arrays, Addierbäume, Pipelines, NoCs, Speicherhierarchien, Taktbereiche, Floorplans. Die Ausgabe ist immer ein editierbarer TikZ-Quellcode plus eine kompilierte PDF/PNG-Vorschau.
Arbeitsablauf
- Paket besorgen. Kopieren Sie
hwfig.styaus dem Basisverzeichnis dieses Skills in das Arbeitsverzeichnis (es sei denn, der Benutzer hat bereits eine Kopie – wenn seine keinev1.1in der\ProvidesPackage-Zeile enthält, bieten Sie ein Update an). Lesen Sie die Datei und kopieren Sie sie byteweise; geben Sie sie niemals von Hand ein oder definieren Sie die Stile direkt neu – die Paletten funktionieren nur über die Rollenfarben. - Palette auswählen (Standard
grey), entweder als Paketoption\usepackage[okabe]{hwfig}oder mit\hwpalette{...}an beliebiger Stelle (gruppenlokal, also pro Figur):grey– Standard für Druck/Zeitschriften, spiegelt die Referenzvorlage wider (weiße Rechenblöcke, graue Quantisierer, gestrichelte graue Konstanten).okabe– Okabe–Ito, farbenblindsicher, stärkster Kontrast. Gute Standardeinstellung für Farbpapiere.tol– Paul Tol muted, farbenblindsicher, weicher.nord– kühle Töne, für Folien.slate– Blaugrau + Malve + Koralle, passend zu wissenschaftlichen Folien. Wenn der Benutzer keine Angabe macht, erstellen Siegreyund schlagen Sie eine Farbpalette vor; zum Vergleichen kompilieren Sie dieselbe Figur in mehreren Paletten (Schleife\foreach \pal in {grey,okabe,...}{\hwpalette{\pal}...}innerhalb einesstandalone-Dokuments ergibt eine Seite pro Palette). Ein unbekannter Palettenname ist ein schwerer Fehler.
- Skizzieren Sie das Layout zuerst auf einem Raster (explizite
at (x,y)-Koordinaten, ~2.4 cm Spaltenabstand, ±1.4 cm Reihen), dann verdrahten. Explizite Koordinaten sind für dichte Datenflüsse besser alspositioning. - Kompilieren als
\documentclass[tikz,border=4pt]{standalone}+\usepackage{hwfig}mitpdflatex -interaction=batchmode -halt-on-error(mit Timeout;nonstopmodekann hängen bleiben), rastern mitpdftoppm -r 110 -png, PNG ansehen und Überlappungen (Beschriftungen auf Leitungen, Leitungen über Blöcken, kollidierende Titel, Rahmenbeschriftungen, die Knoten berühren) vor der Auslieferung beheben. Wiederholen, bis alles sauber ist. - Liefern Sie die
.tex(Figuren-Body, bereit zum Einfügen in ein Paper), die.styund das PDF. Erinnern Sie den Benutzer daran, dass er in seinem Paper\usepackage{hwfig}(oder\usepackage[<palette>]{hwfig}) verwenden muss.
Visuelle Grammatik (konsistent halten)
- Datenfluss links → rechts; der Hauptpfad liegt auf einer horizontalen Achse, parallele Äste oben/unten (±1.4 cm).
- Kompilierzeitkonstanten (
ctime, gestrichelte Box) liegen direkt über/unter dem Block, den sie konfigurieren, und verbinden sich mitdarr(gestrichelter Pfeil). - Jeder Rechenblock, der die Genauigkeit ändert, wird von einem
qbox(Requant/Shift/Saturate) gefolgt. - Äste werden in einem
op-Knoten zusammengeführt, wobei die Leitungen vertikal ankommen:\draw[arr] (qr.east) -| ($(sum.north)+(0,0.32)$) -- (sum.north); - Alternative Äste von einer Quelle: eine Gabelungskoordinate mit einem Punkt,
\fill[hwwire] (fork) circle (1.3pt);, dann|--Knicke zu jedem Ast. - Verwenden Sie
hotfür den kritischen/hervorgehobenen Pfad,busfür breite Datenpfade mitpic[pos=.5]{buswidth=512},node[lbl,midway,above]{AXI4-S}für Protokollnamen (lblbox, wenn die Beschriftung über kreuzenden Leitungen liegen muss). - Gruppierungsrahmen gehen auf die Hintergrundebene mit
fit:\begin{scope}[on background layer]\node[group,fit=(a)(b),label={[grouplabel]above:PL}] {};\end{scope} - Taktbereiche:
clkdomain(Akzentfarbe, gestrichelt) oderclkdomain=<color>; Kreuzungen mit einemcdc-Knoten markieren. - Beginnen Sie jedes Bild mit
\begin{tikzpicture}[hw](setzt Schrift\footnotesize, Linienbreite, Pfeilspitzen).
Routing und Beschriftungen (bei jedem PNG prüfen)
- Abstände zwischen Bereichen. Lassen Sie ≥ 0.6 cm zwischen benachbarten Rahmen (Runtime | Message Passing, PL | AIE). Knoten, Merge-
ops und Leitungen dürfen niemals auf einer Rahmenkante sitzen – wenn ein Knoten zu keinem der Rahmen gehört, geben Sie ihm Platz außerhalb beider. - Routing-Kanäle. Leitungen zwischen Bereichen laufen in einem leeren Kanal zwischen den Rahmen (definieren:
\def\chan{6.7}), dann auf einer eigenen horizontalen Ebene unter/über dem Zielrahmen (\def\lvla{-1.7}). Zwei Leitungen teilen sich nie eine Ebene; halten Sie ≥ 0.3 cm zwischen einer Leitung und einer parallelen Rahmenkante oder Blockseite und ≥ 0.4 cm zwischen parallelen Leitungen. - Beschriftungen nur auf freien Segmenten. Setzen Sie
lbl/buswidthauf ein Segment, das vollständig im leeren Raum liegt – niemals dort, wo die Leitung eine Rahmengrenze kreuzt, und niemals innerhalb von ~0.2 cm eines Blocks. Wenn der einzige freie Abschnitt vertikal ist, drehen Sie die Beschriftung, sodass sie von unten nach oben gelesen wird:node[lbl,rotate=90,below]{AXI4-S}. Bei kurzen Verbindungen verwenden Siepic[pos=.35..0.4], damit die Nummer den Zielblock freigibt. - Explizite Segmente für beschriftete Knicke.
-|/|-kann keine Beschriftung auf einem gewählten Schenkel aufnehmen; schreiben Sie die Ecken aus:(a.east) -- (\chan,ya) -- node[lbl,...]{X} (\chan,\lvla) -- (b.south|-{(0,\lvla)}) -- (b.south); - Mathematik in Beschriftungen:
$W_{\mathrm{root}}$; zweizeilige Beschriftungen mit\\(alle Stile setzenalign=center).
Knotenstile (alle palettenbewusst)
| Stil | Verwendung |
|---|---|
runtime | Laufzeit-Eingaben/Ausgaben, Ports, Tensoren |
ctime | Kompilierzeitkonstanten: Gewichte, Skalen, Konfiguration |
hlsbox | Rechenkern / HLS-Funktion / AIE-Kern |
qbox | Quantisierer, Requant, Shift+Sat |
op | +, ×, max (Kreis) |
mux, demux | Multiplexer (Eingänge West, Ausgang Ost) / Demultiplexer (gespiegelt) |
mem, bram, uram | On-Chip-Puffer |
fifo | Stream/FIFO |
reg / ff | Register mit Taktsymbol |
lut, dsp | FPGA-Primitive (dsp platziert einen kleinen „DSP"-Tag außerhalb seiner oberen rechten Ecke – Platz lassen) |
extmem | DDR / HBM (Zylinder) |
cpu | PS / ARM / RISC-V (Doppelrahmen) |
host | Host-CPU (PCIe-Seite) |
xbar | AXI-Interconnect / NoC-Bar (\rotatebox{90}{AXI IC}) |
router | NoC-Router/Switch (Raute) |
pe | Verarbeitungselement |
aie | AI-Engine-Tile (Kern + lokaler Speicher; Beschriftung zentriert auf dem Kern) |
chip | Gehäuse/Die mit Pins |
port | kleiner Pin-Marker |
cdc | Taktbereichsübergreifender Synchronisierer |
group, plregion, psregion, clkdomain | Hintergrundrahmen |
grouplabel, lbl, lblbox | Rahmentitel, Leitungsbeschriftungen (lblbox = weißer Hintergrund) |
Kantenstile: arr, darr, bus, hot. Bus-Tick: pic[pos=.5]{buswidth=32} (Zahl rechts vom Tick) oder buswidth'=32 (links).
Parametrische Pics
Optionen pro Pic übergeben – sie bleiben lokal für dieses Pic, sodass verschiedene Größen ein Bild teilen können:
\pic (A) at (0,0) {aiearray={rows=2,cols=4}};. \hwset{key=value,...} setzt stattdessen Standardwerte für den Rest des aktuellen Bereichs. Innere Knoten sind adressierbar als (name-...).
| Pic | Schlüssel | Adressierbare Teile |
|---|---|---|
aiearray | rows, cols, dx, dy, tilelabel | Tiles (n-t<r>-<c>) (r=1 unten), Shims (n-s<c>), Rahmen (n-box) |
systolic | rows, cols | (n-pe<r>-<c>) (r=1 oben) |
addertree | depth (2^depth Eingänge) | Eingänge (n-in<k>), Addierer (n-a<level>-<k>), Ausgang (n-out) |
pipeline | labels={Load,MAC,Requant,...} | Stufen (n-st<k>), Register (n-r<k>) |
noc | rows, cols, nocpe (true/false) | Router (n-n<r>-<c>), PEs (n-pe<r>-<c>) |
fabric | columns={I,C,C,D,C,B,...}, fabricrows (beliebiger Wert) | Spaltenköpfe (n-c<k>). C=CLB D=DSP B=BRAM U=URAM I=IO |
legend | legend={comp/Compute,mem/BRAM,...} (Rollen: io comp quant const op mem ctrl group); automatisch beabstandet | — |
Leitungen um Pics herumführen (z.B. von unten in Shim-Tiles: (k1.east) -- ++(0.9,0) |- ($(arr-s1.south)+(0,-0.35)$) -- (arr-s1.south)), niemals durch Tiles.
Benutzerdefinierte Paletten
Keine @-Interna erforderlich – setzen Sie alle 8 Rollen (Draw, Fill) und die drei Leitungsfarben:
\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}} % Leitung, Konstanten-Leitung, Akzent/hervorgehoben
\hwpalette{mine}
Füllungen ≤ 25 % Farbtiefe halten, damit schwarzer Text lesbar bleibt, und die Graustufenkonvertierung prüfen, ob die Rollen noch unterscheidbar sind.
Fallstricke
- Schreiben Sie
.tex-Dateien mit dem Dateischreibwerkzeug, nicht mit Shell-Heredocs,sedoder Python-String-Literalen: Diese verwandeln\\oft in\(oder\c...in Steuerzeichen) und zerstören Zeilenumbrüche im Knotentext. - Ein Doppelpunkt oder Komma innerhalb von
label=-Text unterbricht die Schlüsselanalyse – klammern Sie ihn:label={[grouplabel]above:{INT8: shifts}}. - Eine
fit=-Liste kann nicht($(a)+(1,2)$)enthalten (das Komma teilt die Liste); definieren Sie zuerst eine benannte\coordinate. - Für eine zusätzliche Zeile innerhalb eines Knotens verwenden Sie einen einfachen Knoten mit
align=centerund\\[-2pt]{\tiny ...}– überlagern Sie keine zweite Beschriftung auf der Box. shapes.geometrictrapeziummit leerem Text führt zu einer Division durch Null – deshalb sindmux/demuxbenutzerdefinierte Pfadbilder; tauschen Sie sie nicht zurück.- Verwenden Sie innerhalb von
\foreach\pgfmathtruncatemacrofür ganzzahlige Arithmetik, bevor Sie Werte in Knotennamen verwenden. In\foreach \a/\b in {...}teilt ein/innerhalb des Beschriftungstextes ihn (schreiben SiePort, nichtI/O). clkdomainhat eine helle Füllung; übergeben Siefill=none, wenn Sie es über einemfabricoder anderen dichten Inhalten zeichnen.- MiKTeX ignoriert POSIX-
TEXINPUTS; behalten Siehwfig.styneben der.tex(oder übergeben Sie--include-directory=<windows path>). - Halten Sie Figuren ≤ ~16 cm breit für ein zweispaltiges Paper bei
\footnotesize; skalieren Sie mit\resizebox{\linewidth}{!}{...}nur als letztes Mittel.
Referenzbeispiel (GNN-Schicht, die Hausvorlage)
\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}
Referenzbeispiel (Versal SoC)
\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};
% routing levels
\def\chan{6.7} % vertical channel between PL and AIE regions
\def\lvla{-1.7} % mm2s -> AIE, under the AIE frame
\def\lvlb{-2.3} % AIE -> s2mm, one level lower (= s2mm row)
\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: right into the channel, down, along level a, up into the 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: down to level b, left to 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}
Paketquelle
hwfig.sty (v1.1, von Pelayo Leguina, [email protected], LPPL 1.3c) befindet sich neben dieser Datei. Changelog, Galerie und Beispiele leben im öffentlichen hwfig-Repository; dieses Skill wird dort unter skill/ gepflegt und mit skill/package.sh verpackt.