TikZ 硬件图形(hwfig v1.1)
当用户希望使用 LaTeX/TikZ 创建硬件图形时使用此技能:HLS/FPGA 内核的数据流、量化神经网络层、Versal/Zynq SoC 框图、AI Engine 阵列、脉动阵列、加法器树、流水线、NoC、存储器层次结构、时钟域、平面图。输出始终是可编辑的 TikZ 源码以及编译的 PDF/PNG 预览。
工作流程
- 获取包。 将此技能基础目录中的
hwfig.sty复制到工作目录(除非用户已有副本——如果他们的副本的\ProvidesPackage行中没有v1.1,则提供更新)。读取文件并逐字节复制;绝不要重新输入或内联重新定义样式——调色板仅通过角色颜色生效。 - 选择调色板(默认为
grey),可以作为包选项\usepackage[okabe]{hwfig}或使用\hwpalette{...}在任何位置设置(组本地,因此每个图形独享):grey— 打印/期刊默认,镜像参考模板(白色计算框、灰色量化器、虚线灰色常量)。okabe— Okabe–Ito,色盲安全,对比度最强。彩色论文的良好默认选项。tol— Paul Tol 柔和色调,色盲安全,更柔和。nord— 冷色调,用于幻灯片。slate— 蓝灰 + 紫红 + 珊瑚色,与科学幻灯片模板匹配。 如果用户没有指定,则生成grey并提供一个彩色调色板;要进行比较,可以在多个调色板中编译同一图形(在standalone文档内部使用循环\foreach \pal in {grey,okabe,...}{\hwpalette{\pal}...}会为每个调色板生成一页)。未知的调色板名称会导致硬错误。
- 首先在网格上绘制布局草图(显式
at (x,y)坐标,列间距约 2.4 cm,行间距 ±1.4 cm),然后布线。对于密集数据流图形,显式坐标优于positioning。 - 编译为
\documentclass[tikz,border=4pt]{standalone}+\usepackage{hwfig},使用pdflatex -interaction=batchmode -halt-on-error(带超时;nonstopmode可能挂起),使用pdftoppm -r 110 -png栅格化,查看 PNG,并在交付前修复重叠(导线上的标签、穿过盒子的导线、碰撞的标题、接触节点的框架标签)。迭代直到图形清晰。 - 交付
.tex(可直接粘贴到论文中的图形主体)、.sty和 PDF。提醒用户在他们的论文中需要\usepackage{hwfig}(或\usepackage[<调色板>]{hwfig})。
视觉语法(保持一致性)
- 数据流为从左到右;主路径位于水平轴上,平行分支位于上方/下方(±1.4 cm)。
- 编译时常量(
ctime,虚线框)直接位于其配置的块上方/下方,并通过darr(虚线箭头)连接。 - 每个改变精度的计算块后都跟一个
qbox(重量化/移位/饱和)。 - 分支汇入
op节点,使用垂直接近的肘形导线:\draw[arr] (qr.east) -| ($(sum.north)+(0,0.32)$) -- (sum.north); - 来自同一源的不同分支:使用带点的分叉坐标,
\fill[hwwire] (fork) circle (1.3pt);,然后使用|-肘形连接到每个分支。 - 使用
hot表示关键/高亮路径,使用bus表示宽数据通路,并搭配pic[pos=.5]{buswidth=512},使用node[lbl,midway,above]{AXI4-S}表示协议名称(当标签必须位于交叉导线上方时使用lblbox)。 - 分组框架放在背景层,使用
fit:\begin{scope}[on background layer]\node[group,fit=(a)(b),label={[grouplabel]above:PL}] {};\end{scope} - 时钟域:
clkdomain(强调色,虚线)或clkdomain=<color>;使用cdc节点标记交叉点。 - 每个图片以
\begin{tikzpicture}[hw]开头(设置字体\footnotesize、线宽、箭头提示)。
布线和标签(在每个 PNG 上检查这些)
- 区域间的间隙。 相邻框架之间保留 ≥ 0.6 cm(Runtime | Message passing, PL | AIE)。节点、合并
op和导线绝不得位于框架边缘上——如果节点不属于任一框架,则在其外部留出空间。 - 布线通道。 区域之间的导线在框架间的空通道中运行(定义它:
\def\chan{6.7}),然后在目标框架下方/上方自己的水平层级上运行(\def\lvla{-1.7})。两条导线绝不共享一个层级;导线与任何平行框架边缘或块侧面之间保持 ≥ 0.3 cm,平行导线之间保持 ≥ 0.4 cm。 - 标签仅位于空闲线段上。 将
lbl/buswidth放在完全位于空白空间中的线段上——绝不要在导线穿过框架边界的位置,也绝不要在距离块约 0.2 cm 范围内。如果唯一的空闲段是垂直的,则将标签旋转为从下到上阅读:node[lbl,rotate=90,below]{AXI4-S}。在短链路上使用pic[pos=.35..0.4]以便数字避开目标块。 - 为带标签的肘形使用显式线段。
-|/|-无法在选定的支腿上使用标签;将拐角写出来:(a.east) -- (\chan,ya) -- node[lbl,...]{X} (\chan,\lvla) -- (b.south|-{(0,\lvla)}) -- (b.south); - 标签中的数学公式:
$W_{\mathrm{root}}$;使用\\实现两行标签(所有样式都设置了align=center)。
节点样式(所有样式均受调色板影响)
| 样式 | 用途 |
|---|---|
runtime | 运行时输入/输出、端口、张量 |
ctime | 编译时常量:权重、缩放因子、配置 |
hlsbox | 计算内核 / HLS 函数 / AIE 内核 |
qbox | 量化器、重量化、移位+饱和 |
op | +, ×, max(圆形) |
mux, demux | 多路复用器(输入在西,输出在东)/ 解复用器(镜像) |
mem, bram, uram | 片上缓冲器 |
fifo | 流/FIFO |
reg / ff | 带时钟楔形的寄存器 |
lut, dsp | FPGA 原语(dsp 会在其右上角外侧放置一个小的 "DSP" 标签——留出空间) |
extmem | DDR / HBM(圆柱体) |
cpu | PS / ARM / RISC-V(双框) |
host | 主机 CPU(PCIe 侧) |
xbar | AXI 互连 / NoC 条(\rotatebox{90}{AXI IC}) |
router | NoC 路由器/交换机(菱形) |
pe | 处理单元 |
aie | AI Engine 瓦片(核心 + 本地存储器;标签居中对齐于核心) |
chip | 封装/芯片,带引脚 |
port | 小引脚标记 |
cdc | 时钟域交叉同步器 |
group, plregion, psregion, clkdomain | 背景框架 |
grouplabel, lbl, lblbox | 框架标题、导线标签(lblbox = 白色背景) |
边样式:arr, darr, bus, hot。总线刻度标记:pic[pos=.5]{buswidth=32}(数字在刻度标记右侧)或 buswidth'=32(左侧)。
参数化图片
每个图片可以传递选项——它们仅在该图片内局部生效,因此不同尺寸可以共享同一图片:
\pic (A) at (0,0) {aiearray={rows=2,cols=4}};。使用 \hwset{key=value,...} 可以为当前作用域的其余部分设置默认值。内部节点可以通过 (name-...) 进行寻址。
| 图片 | 键 | 可寻址部分 |
|---|---|---|
aiearray | rows, cols, dx, dy, tilelabel | 瓦片 (n-t<r>-<c>)(r=1 为底部),shim (n-s<c>),框架 (n-box) |
systolic | rows, cols | (n-pe<r>-<c>)(r=1 为顶部) |
addertree | depth(2^depth 输入) | 输入 (n-in<k>),加法器 (n-a<level>-<k>),输出 (n-out) |
pipeline | labels={Load,MAC,Requant,...} | 级 (n-st<k>),寄存器 (n-r<k>) |
noc | rows, cols, nocpe(true/false) | 路由器 (n-n<r>-<c>),PE (n-pe<r>-<c>) |
fabric | columns={I,C,C,D,C,B,...}, fabricrows(任意值) | 列顶部 (n-c<k>)。C=CLB D=DSP B=BRAM U=URAM I=IO |
legend | legend={comp/Compute,mem/BRAM,...}(角色:io comp quant const op mem ctrl group);自动间距 | — |
导线应绕过图片布线(例如,从下方进入 shim 瓦片:(k1.east) -- ++(0.9,0) |- ($(arr-s1.south)+(0,-0.35)$) -- (arr-s1.south)),切勿穿过瓦片。
自定义调色板
不需要 @ 内部变量——设置所有 8 个角色(绘制、填充)和三种导线颜色:
\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}} % 导线、常量导线、强调色/高亮
\hwpalette{mine}
保持填充色 ≤ 25% 色调,以便黑色文本保持可读,并检查灰度转换后角色是否仍然可区分。
常见陷阱
- 使用文件写入工具编写
.tex文件,而不是 shell heredoc、sed或 Python 字符串字面量:这些工具经常将\\变成\(或将\c...变成控制字符)并破坏节点文本中的换行符。 label=文本中的冒号或逗号会破坏键解析——用花括号括起来:label={[grouplabel]above:{INT8: shifts}}。fit=列表不能包含($(a)+(1,2)$)(逗号会分割列表);先定义一个命名的\coordinate。- 要在节点内添加额外行,请使用带有
align=center和\\[-2pt]{\tiny ...}的普通节点——不要在盒子上叠加第二个标签。 shapes.geometric的trapezium在文本为空时会除以零——这就是为什么mux/demux是自定义路径图片;不要将它们改回去。- 在
\foreach内部,在节点名称中使用值之前,使用\pgfmathtruncatemacro进行整数算术;在\foreach \a/\b in {...}中,标签文本内部的/会分割它(写成Port,而不是I/O)。 clkdomain具有浅色填充;当它在fabric或其他密集内容上绘制时,传递fill=none。- MiKTeX 忽略 POSIX 风格的
TEXINPUTS;将hwfig.sty放在.tex旁边(或传递--include-directory=<windows path>)。 - 对于使用
\footnotesize的双栏论文,将图形宽度保持在约 16 cm 以内;仅作为最后手段使用\resizebox{\linewidth}{!}{...}缩放。
参考示例(GNN 层,模板)
\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}
参考示例(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};
% 布线层级
\def\chan{6.7} % PL 和 AIE 区域之间的垂直通道
\def\lvla{-1.7} % mm2s -> AIE,在 AIE 框架下方
\def\lvlb{-2.3} % AIE -> s2mm,再低一层(= 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:向右进入通道,向下,沿层级 a,向上进入 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:向下到层级 b,向左到 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}
包源码
hwfig.sty(v1.1,作者 Pelayo Leguina,[email protected],LPPL 1.3c)与此文件一同发布。更新日志、图库和示例位于公开的 hwfig 仓库中;此技能在该仓库的 skill/ 目录下维护,并通过 skill/package.sh 打包。