<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Kintex-7 on AI博士 万戈</title>
        <link>https://www.yesmiracle.net/tags/kintex-7/</link>
        <description>AI博士万戈的技术博客，聚焦 Agentic AI、AI Infra 与 Agent Security，分享 AI 基础设施与工程落地实践。</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <managingEditor>admin@yesmiracle.net (万戈)</managingEditor>
        <webMaster>admin@yesmiracle.net (万戈)</webMaster>
        <lastBuildDate>Wed, 07 Oct 2026 12:10:00 +1000</lastBuildDate><atom:link href="https://www.yesmiracle.net/tags/kintex-7/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>openTPU 深度拆解：AI 设计的开源 AI 加速器，怎么把 Qwen3 跑在 Kintex-7 上</title>
        <link>https://www.yesmiracle.net/post/20261007-opentpu-open-fpga-ai-accelerator-deep-dive/</link>
        <pubDate>Wed, 07 Oct 2026 12:10:00 +1000</pubDate>
        <author>admin@yesmiracle.net (万戈)</author>
        <guid>https://www.yesmiracle.net/post/20261007-opentpu-open-fpga-ai-accelerator-deep-dive/</guid>
        <description>&lt;img src="https://www.yesmiracle.net/post/20261007-opentpu-open-fpga-ai-accelerator-deep-dive/cover.svg" alt="Featured image of post openTPU 深度拆解：AI 设计的开源 AI 加速器，怎么把 Qwen3 跑在 Kintex-7 上" /&gt;&lt;p&gt;昨天在 HN 上翻到一个仓库，标题只有一句话：&lt;em&gt;An open-source AI accelerator, developed by AI.&lt;/em&gt;（一个由 AI 开发的、开源的 AI 加速器。）&lt;/p&gt;
&lt;p&gt;我一开始是抱着看热闹的心态点进去的，毕竟「AI 自己设计芯片」这种说法今年听得太多了。但把这个 &lt;code&gt;FeSens/openTPU&lt;/code&gt; 仓库读了一遍之后，我改了看法——它真正有意思的地方根本不是那句口号，而是它把一个 AI 加速器该有的东西全塞进了一个你能从头读到尾的 monorepo：SystemVerilog 硬件、一条自研的指令集、一个 bit-exact 的模拟器、一门叫 &lt;code&gt;ol&lt;/code&gt; 的 kernel 语言和它的编译器、还有驱动真实 PCIe 卡的 host 软件。&lt;/p&gt;
&lt;p&gt;它甚至真的在一张 Kintex-7 的 FPGA 卡上把 Qwen3-0.6B 和 LFM2.5-230M 跑了起来，而且卡上吐出来的 token 和模拟器&lt;strong&gt;逐 bit 一致&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这句话是全文最值得盯的地方，我后面会展开。&lt;/p&gt;
&lt;p&gt;先把这个仓库几个容易踩的坑说清楚。openTPU 这个名字现在不止一个：GitHub 上排第一的是这个 FeSens/openTPU（2026-09-24 才建仓，两百多 star），另一个是 SKYNETAI1/OpenTPU（8 月建仓，定位是 ASIC-first 的 LLM 推理 IP，只有 4 个 star）。两者没有任何关系，我这篇讲的是前者。另外，仓库里那个 &lt;code&gt;developed by AI&lt;/code&gt; 标签指的是&lt;strong&gt;硬件设计过程本身&lt;/strong&gt;用 AI agent 来做，不是「这颗芯片的速度来自 AI」。这个区别值得单独讲。&lt;/p&gt;
&lt;h2 id=&#34;developed-by-ai到底指什么&#34;&gt;「developed by AI」到底指什么&lt;/h2&gt;
&lt;p&gt;口号容易让人误会。openTPU 说它把 &lt;code&gt;auto-arch-tournament&lt;/code&gt; 的经验搬到了加速器上，问两个问题：AI agent 在硬件设计上能走多远，以及——能不能造出跑自己推理的芯片。&lt;/p&gt;
&lt;p&gt;落到代码里，这指的是 &lt;code&gt;tools/tourney/&lt;/code&gt; 那一套。它的工作方式是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个 RTL 组件（&lt;code&gt;otpu_mxu&lt;/code&gt;、&lt;code&gt;otpu_vpu&lt;/code&gt;、&lt;code&gt;otpu_quant&lt;/code&gt;、&lt;code&gt;otpu_seq&lt;/code&gt;、&lt;code&gt;otpu_tmem&lt;/code&gt;、&lt;code&gt;otpu_dma&lt;/code&gt;、&lt;code&gt;otpu_fp&lt;/code&gt; 等）各有一个自己的 champion 分支；&lt;/li&gt;
&lt;li&gt;每一轮，K 个 agent 并行地在各自 worktree 里提出假设、改 RTL、跑门禁；&lt;/li&gt;
&lt;li&gt;假设 agent 只准写 &lt;code&gt;HYPOTHESIS.md&lt;/code&gt;，实现 agent 只准改允许的文件——改了别的文件，这个 slot 直接判 failed；&lt;/li&gt;
&lt;li&gt;门禁按顺序跑：sandbox（只许改本组件的文件）→ lint（Verilator）→ fast（RTL 与 ISA 的 bit-exact 子集测试）→ board（用板级微架构再跑一遍）→ perf（Qwen3 decode 代理，循环数不许比 champion 多 0.2%）→ synth（yosys 出 LUT/FF/DSP/BRAM 和 fmax 估计）；&lt;/li&gt;
&lt;li&gt;最后按接受规则挑赢家：A 是面积赢（fmax 达标且面积等效至少 -1%），B 是速度赢（fmax +3% 且面积至多 +1%）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那个 &lt;code&gt;area_eq&lt;/code&gt; 是个挺有意思的工程决定，它把不同资源折算成一个数：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;area_eq = LUT + LUTRAM + 0.5·FF + 40·DSP + 80·BRAM36 + 40·BRAM18
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;fmax 的估计则是 &lt;code&gt;1000 / (1.6·logic_ns + 0.5)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;这些系数（40 一个 DSP、80 一个 BRAM36）是作者对这块器件成本结构的经验假设。你可以不同意，但重点在于它被写死成规则、每一轮都一致地执行——不然 agent 之间没法比较。&lt;/p&gt;
&lt;p&gt;我的判断是：这才是「developed by AI」真正的分量。它不是让 AI 去写一篇关于加速器的论文，而是把「改硬件 → 跑正确性 → 测性能 → 综合出面积和频率 → 择优」这个闭环做成了一条自动化流水线，让 agent 在一个有明确奖励函数的空间里搜索。而人类仍然握着最后一道闸：champion 分支永远不进 &lt;code&gt;main&lt;/code&gt;，把一个 champion 合回主线是人工决定。&lt;/p&gt;
&lt;h2 id=&#34;整个栈长什么样&#34;&gt;整个栈长什么样&lt;/h2&gt;
&lt;p&gt;作者在 README 里画了一张从 Python kernel 一直到 FPGA 引脚的图，我照着整理了一下：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;  Kernels in ol              mlp, attention, full model layers
        |  @ol.jit
  Language + compiler        layouts, affine loop addressing, fusion
        |
  ISA                        8 x 32-bit words per instruction
        |
  ISA simulator  &amp;lt;======&amp;gt;  RTL          same bits, checked by the tests
  (Python)                 (SystemVerilog)
                            |  Vivado bitstream
                           FPGA card    Kintex-7 xc7k480t
                            |  PCIe
                           Host         otpu-chat, otpu-smi, otpu-lens
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;从上到下几层，各自是什么：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;层&lt;/th&gt;
          &lt;th&gt;实现&lt;/th&gt;
          &lt;th&gt;关键文件&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;kernel&lt;/td&gt;
          &lt;td&gt;一门叫 &lt;code&gt;ol&lt;/code&gt; 的 Python DSL&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;opentpu/kernels/*.py&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;编译&lt;/td&gt;
          &lt;td&gt;把 &lt;code&gt;ol&lt;/code&gt; 降到数据搬运指令（Triton/Gluon 风格）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;opentpu/compiler.py&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;ISA&lt;/td&gt;
          &lt;td&gt;自研指令集，每条指令 8 个 32-bit 字&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;opentpu/isa.py&lt;/code&gt;、&lt;code&gt;docs/isa.md&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;模拟器&lt;/td&gt;
          &lt;td&gt;ISA 的 Python 参考实现（模拟器即规范）&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;opentpu/isasim.py&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;RTL&lt;/td&gt;
          &lt;td&gt;SystemVerilog 硬件，和模拟器逐 bit 对齐&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;rtl/top/otpu_top.sv&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;板&lt;/td&gt;
          &lt;td&gt;Inspur YPCB-00338 / Kintex-7 xc7k480t + 两通道 DDR3&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;boards/ypcb-00338/&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;host&lt;/td&gt;
          &lt;td&gt;PCIe 驱动与用户态工具&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;opentpu/host/&lt;/code&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;整条链上最硬的约束是：&lt;strong&gt;ISA 模拟器、RTL 和真实的卡，三者必须对每一个写进 TMEM 或 DRAM 的 bit 达成一致&lt;/strong&gt;。这不是「大致对齐」，是逐 bit。后面你会看到，这个约束决定了整个设计里几乎每一个算术细节。&lt;/p&gt;
&lt;h2 id=&#34;指令集没有-cache也没有隐藏调度&#34;&gt;指令集：没有 cache，也没有隐藏调度&lt;/h2&gt;
&lt;p&gt;我花时间最多的是 &lt;code&gt;docs/isa.md&lt;/code&gt;，因为作者自己说指令集是一切的底座。&lt;/p&gt;
&lt;p&gt;机器模型是这样：有 &lt;code&gt;S&lt;/code&gt; 个 slice，每个 slice 里有一个 16 × 32-bit 寄存器的 sequencer（&lt;code&gt;R0&lt;/code&gt; 恒读 0）、指令内存、一片私有的 DRAM、TMEM、ACT RAM，以及一个 MXU（矩阵单元）、一个 VPU（向量单元）和一个 quantizer。slice 之间除了一个 collective 单元（&lt;code&gt;GATHER&lt;/code&gt;、&lt;code&gt;BAR&lt;/code&gt;）不共享任何东西。执行严格顺序：每条指令写完所有结果，下一条才开始。&lt;/p&gt;
&lt;p&gt;每条指令编码成 8 个 32-bit 字，字 0 长这样：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;w0 = opcode[7:0] | ra[11:8] | rb[15:12] | rc[19:16] | rd[23:20] | flags[31:24]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;地址一律是「寄存器 + 立即数」。&lt;/p&gt;
&lt;p&gt;这里有个我很喜欢的决定：&lt;strong&gt;没有 cache，也没有隐藏调度&lt;/strong&gt;。数据怎么动，全是一条一条指令写死的：DMA 搬数据是 &lt;code&gt;LD&lt;/code&gt;/&lt;code&gt;ST&lt;/code&gt;，矩阵单元吃从 DRAM 流进来的 int8 权重是 &lt;code&gt;MM&lt;/code&gt;，向量单元做 fp32 数学是 &lt;code&gt;VOP&lt;/code&gt;，quantizer 把结果转回 int8 是 &lt;code&gt;QACT&lt;/code&gt;。作者的原话是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There is no cache and no hidden scheduling: every data movement is an instruction, so a trace shows exactly where the cycles go.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;每一次数据搬动都是一条指令，所以一条 trace 就能直接告诉你时间花在哪了。对于一个教学和研究取向的设计来说，这比性能更重要——它让「为什么这一步慢」变成一个可以逐指令回答的问题，而不是丢给你一句「去 profile 吧」。&lt;/p&gt;
&lt;p&gt;还有个细节让我确认作者是认真在设计 bit-exact：fp32 算术全部是 &lt;strong&gt;flush to zero&lt;/strong&gt;（denormal 输入当带符号零、denormal 结果替换成带符号零），而且 &lt;code&gt;exp2&lt;/code&gt;、&lt;code&gt;recip&lt;/code&gt;、&lt;code&gt;rsqrt&lt;/code&gt;、&lt;code&gt;log2&lt;/code&gt; 这些超越函数被&lt;strong&gt;定义成固定的 add/mul 序列&lt;/strong&gt;，好让每个实现都 bit-exact。比如 &lt;code&gt;rsqrt&lt;/code&gt; 用的是那个著名的 &lt;code&gt;0x5F3759DF&lt;/code&gt; 魔数种子，然后固定做三次牛顿迭代，再按指数缩放。规范甚至把 &lt;code&gt;log2&lt;/code&gt; 的 minimax 拟合系数逐位列了出来：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;3FB8AA3B BF38AA38 3EF639EB BEB8AE27 3E9369C2 BE74ADF2 3E5CE48E BE543E8E 3E00DB73
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;读到这里我就明白了：如果目标是「卡和模拟器逐 bit 一致」，那每一个浮点步骤、每一次舍入都必须被规定到 bit，否则模拟器的结果就没有意义。这是被那条硬约束逼出来的设计，不是为了炫技。&lt;/p&gt;
&lt;h2 id=&#34;kernel-和编译器layout-就是数据搬运&#34;&gt;kernel 和编译器：layout 就是数据搬运&lt;/h2&gt;
&lt;p&gt;上层是一门叫 &lt;code&gt;ol&lt;/code&gt; 的语言，写法很像 Triton/Gluon。一个 kernel 就是 &lt;code&gt;@ol.jit&lt;/code&gt; 装饰的 Python 函数，按 slice 各 trace 一次，每个调用降成一到几条 ISA 指令。&lt;/p&gt;
&lt;p&gt;编译器的核心概念是 &lt;strong&gt;layout&lt;/strong&gt;：张量住在哪里，就是它的 layout；改变 layout 本身，就是一条数据搬运指令。作者列了一张表：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;调用&lt;/th&gt;
          &lt;th&gt;指令&lt;/th&gt;
          &lt;th&gt;搬运&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ol.load(t)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;LD&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;DRAM → TMEM，burst&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ol.store(t, x)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;ST&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;TMEM → DRAM，burst&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ol.quantize(x)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;QACT&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;TMEM fp32 → ACT RAM int8&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ol.dot(a, w, ...)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;MM&lt;/code&gt;（必要时加 &lt;code&gt;QACT&lt;/code&gt;）&lt;/td&gt;
          &lt;td&gt;把 &lt;code&gt;w&lt;/code&gt; 从 DRAM 流一次，结果进 TMEM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;x + y&lt;/code&gt;、&lt;code&gt;ol.exp2&lt;/code&gt;、&lt;code&gt;ol.max&lt;/code&gt;…&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;VOP&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;TMEM → TMEM&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;code&gt;ol.all_gather(x)&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;GATHER&lt;/code&gt;&lt;/td&gt;
          &lt;td&gt;每个 slice 的 TMEM → 所有 slice 的 TMEM&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一个 MLP 的 kernel 长这样：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;from&lt;/span&gt; opentpu &lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; language &lt;span style=&#34;color:#66d9ef&#34;&gt;as&lt;/span&gt; ol
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@ol.jit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mlp&lt;/span&gt;(h, gamma, w_gate, w_up, w_down, out, eps):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    x &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load(h)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    xs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;quantize(rmsnorm(x, ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load(gamma), eps))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    g &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dot(xs, w_gate)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    u &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dot(xs, w_up)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all_gather(silu(g) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; u)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    y &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;all_gather(ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dot(a, w_down))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;program_id() &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;store(out, x &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; y)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;真正让我停下来的是 flash attention 那段。&lt;code&gt;opentpu/kernels/attention.py&lt;/code&gt; 是一个 online-softmax（flash）attention，按 FlashAttention-3 的风格做了软件流水：VPU 和 quantizer 还在收尾 block b 的时候，MXU 已经在把 block b+1 的 &lt;code&gt;q.K^T&lt;/code&gt; 往另一个 score buffer 里流了。循环体覆盖两个 block，所以每个临时量都是双缓冲：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;scores&lt;/span&gt;(t0, n, out):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dot(qs, K[t0:t0 &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; n, :], out&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;out, rowmax&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;finish&lt;/span&gt;(s, t0, n):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    vs &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;load(VS[t0:t0 &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; n])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    m_new &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;maximum(m, s&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;rowmax)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    p &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exp2(s &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; m_new[:, &lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    alpha &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;exp2(m &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt; m_new)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    pq &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;quantize(p &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; vs[&lt;span style=&#34;color:#66d9ef&#34;&gt;None&lt;/span&gt;, :])
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;dot(pq, VT[:, t0:t0 &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; n], acc&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;acc, acc_scale&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;alpha)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set(l &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; alpha &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; ol&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sum(p, axis&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    m&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;set(m_new)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意 &lt;code&gt;rowmax=True&lt;/code&gt;——softmax 的 max 被折进了 &lt;code&gt;MM&lt;/code&gt; 指令本身的 RMAX epilogue 标志位里，一次矩阵乘顺便把行最大值也写出来，省掉一趟单独遍历。类似的还有 &lt;code&gt;acc&lt;/code&gt; 加 &lt;code&gt;acc_scale=alpha&lt;/code&gt;，把 &lt;code&gt;acc = acc*alpha + P.V&lt;/code&gt; 的 rescale 也塞进 MM 的 drain 阶段。&lt;/p&gt;
&lt;p&gt;对 4 个 query head、d=32、32-token block、T=160 的情况，作者用 &lt;code&gt;opentpu.isa.disassemble&lt;/code&gt; 打出来的循环体是 &lt;strong&gt;22 条指令加上 3 条地址步进&lt;/strong&gt;。这个数字很能说明问题：从一门 Python DSL 到 22 条机器指令，中间没有任何被藏起来的调度或运行时。&lt;/p&gt;
&lt;h2 id=&#34;mxu一个会流动的二维-systolic-阵列&#34;&gt;MXU：一个会「流动」的二维 systolic 阵列&lt;/h2&gt;
&lt;p&gt;矩阵单元是整个设计里最吃资源的部分，也是作者记录得最细的。默认实现（&lt;code&gt;MXU_IMPL=2&lt;/code&gt;）是一个 D × MCOLS 的 systolic 阵列：每推进一次，一个 D=128 字节的权重 chunk 去碰 MCOLS 个 activation block，每列把 128 个 int8 × int8 的乘积精确求和，再跑每列的 fp32 epilogue。&lt;/p&gt;
&lt;p&gt;关键设计是：&lt;strong&gt;权重是流动的，不是广播的&lt;/strong&gt;。chunk 只解码一次，在每条链的每个 stage 上 skew 一次，然后每过一个 column 就移动一个寄存器 hop。第 j 列在第 0 列之后 j 个 cycle 才看到它。每个 hop 寄存器只驱动一个 DSP 和下一个 hop——扇出是 2，不是 MCOLS。作者在文档里写明，正是原来的「广播 + fabric 加法树」让 MCOLS=4 都过不了时序。&lt;/p&gt;
&lt;p&gt;4-bit 权重也有专门的通路（&lt;code&gt;PAIR&lt;/code&gt;）：列 j &amp;lt; M 取 chunk 的低 block，j &amp;gt;= M 取高 block，选择动作放在 DSP48E1 自己的 pre-adder 里做，省掉了每个位置的 fabric mux。&lt;/p&gt;
&lt;p&gt;代价是 DSP 用得多：每列 128 个乘积 DSP、4 个 sub-block 乘子、外加 epilogue 的 fp 乘子，一共 138 个；MCOLS=8 时是 1106 个 DSP，占这块器件 1920 个 DSP 的一大半（其余设计才用约 150 个）。&lt;/p&gt;
&lt;p&gt;作者把 MXU 单独拉出来做了 OOC 综合（Vivado 2026.1，xc7k480t-2，150 MHz 时钟，D=128），结果是这样：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;MXU&lt;/th&gt;
          &lt;th&gt;MCOLS&lt;/th&gt;
          &lt;th&gt;LUT&lt;/th&gt;
          &lt;th&gt;FF&lt;/th&gt;
          &lt;th&gt;slices&lt;/th&gt;
          &lt;th&gt;DSP&lt;/th&gt;
          &lt;th&gt;fmax&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;IMPL 0（加法树）&lt;/td&gt;
          &lt;td&gt;4&lt;/td&gt;
          &lt;td&gt;26,389&lt;/td&gt;
          &lt;td&gt;15,504&lt;/td&gt;
          &lt;td&gt;9,479&lt;/td&gt;
          &lt;td&gt;282&lt;/td&gt;
          &lt;td&gt;153.5 MHz&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;IMPL 2, use_dsp&lt;/td&gt;
          &lt;td&gt;4&lt;/td&gt;
          &lt;td&gt;24,426&lt;/td&gt;
          &lt;td&gt;31,540&lt;/td&gt;
          &lt;td&gt;12,232&lt;/td&gt;
          &lt;td&gt;554&lt;/td&gt;
          &lt;td&gt;157.9 MHz&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;IMPL 2, use_dsp&lt;/td&gt;
          &lt;td&gt;8&lt;/td&gt;
          &lt;td&gt;47,822&lt;/td&gt;
          &lt;td&gt;60,926&lt;/td&gt;
          &lt;td&gt;23,706&lt;/td&gt;
          &lt;td&gt;1,106&lt;/td&gt;
          &lt;td&gt;151.0 MHz&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;IMPL 2, DSP48E1&lt;/td&gt;
          &lt;td&gt;8&lt;/td&gt;
          &lt;td&gt;44,779&lt;/td&gt;
          &lt;td&gt;67,997&lt;/td&gt;
          &lt;td&gt;24,214&lt;/td&gt;
          &lt;td&gt;1,106&lt;/td&gt;
          &lt;td&gt;151.5 MHz&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;从 MCOLS=4 扩到 8，每列多花约 5.6K LUT、8.3K FF、2.5K slices 和 138 个 DSP。拿这些数字去对比一颗 2013 年发布的 Kintex-7，你会很直观地感受到：这是一个「能不能装得下」的工程，而不是「谁的算力更大」的比赛。&lt;/p&gt;
&lt;h2 id=&#34;性能真实的卡真实的数字&#34;&gt;性能：真实的卡，真实的数字&lt;/h2&gt;
&lt;p&gt;作者在一张 Inspur YPCB-00338 卡（Kintex-7 xc7k480t 加两条 DDR3 通道）上跑了十个模型，权重用的是真实权重。我挑几个关键配置整理成表：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;模型&lt;/th&gt;
          &lt;th&gt;权重&lt;/th&gt;
          &lt;th&gt;Decode(设备)&lt;/th&gt;
          &lt;th&gt;Decode(wall)&lt;/th&gt;
          &lt;th&gt;Prefill&lt;/th&gt;
          &lt;th&gt;Decode 时 DRAM&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;LFM2.5-230M&lt;/td&gt;
          &lt;td&gt;int8&lt;/td&gt;
          &lt;td&gt;59.0 tok/s&lt;/td&gt;
          &lt;td&gt;52.3 tok/s&lt;/td&gt;
          &lt;td&gt;295.6 tok/s&lt;/td&gt;
          &lt;td&gt;14.5 GB/s (85%)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;LFM2.5-230M&lt;/td&gt;
          &lt;td&gt;4-bit&lt;/td&gt;
          &lt;td&gt;85.8 tok/s&lt;/td&gt;
          &lt;td&gt;82.1 tok/s&lt;/td&gt;
          &lt;td&gt;335.4 tok/s&lt;/td&gt;
          &lt;td&gt;14.1 GB/s (82%)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Qwen3-0.6B&lt;/td&gt;
          &lt;td&gt;int8&lt;/td&gt;
          &lt;td&gt;21.6 tok/s&lt;/td&gt;
          &lt;td&gt;21.3 tok/s&lt;/td&gt;
          &lt;td&gt;92.1 tok/s&lt;/td&gt;
          &lt;td&gt;14.4 GB/s (84%)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Qwen3-0.6B&lt;/td&gt;
          &lt;td&gt;4-bit&lt;/td&gt;
          &lt;td&gt;31.3 tok/s&lt;/td&gt;
          &lt;td&gt;30.7 tok/s&lt;/td&gt;
          &lt;td&gt;103.4 tok/s&lt;/td&gt;
          &lt;td&gt;13.9 GB/s (82%)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Qwen3.5-0.8B&lt;/td&gt;
          &lt;td&gt;4-bit&lt;/td&gt;
          &lt;td&gt;24.5 tok/s&lt;/td&gt;
          &lt;td&gt;23.3 tok/s&lt;/td&gt;
          &lt;td&gt;66.7 tok/s&lt;/td&gt;
          &lt;td&gt;14.1 GB/s (83%)&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Phi-4-mini (3.8B)&lt;/td&gt;
          &lt;td&gt;int8&lt;/td&gt;
          &lt;td&gt;3.99 tok/s&lt;/td&gt;
          &lt;td&gt;3.98 tok/s&lt;/td&gt;
          &lt;td&gt;13.8 tok/s&lt;/td&gt;
          &lt;td&gt;16.0 GB/s (94%)&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;这张表里最重要的不是 tok/s，是最后一列。&lt;strong&gt;几乎所有配置在 decode 时都把 DDR3 打到了 82–94% 的峰值带宽&lt;/strong&gt;。作者自己也点破了：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Decode is bound by DRAM.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;decode 是被 DRAM 带宽绑死的，不是被算力。这个结论解释了一个很反直觉的现象：把 MXU 的 MCOLS 从 4 加到 8（算力翻倍），decode 一点都不会变快——瓶颈根本不在那里。它也解释了作者为什么把工程重心放在 LiteDRAM 控制器的校准、DRAM burst 效率、以及 4-bit 量化上：把每 token 的字节数砍掉约三分之一，直接换来 40–45% 的 decode 提速。&lt;/p&gt;
&lt;p&gt;在一个只有两条 DDR3-1066（17.1 GB/s 峰值）的 2013 年器件面前，这几乎是一道物理墙。4-bit 量化之所以能把 LFM2.5-230M 从 59 干到 85.8 tok/s，本质上就是它在同一道内存墙前面少搬了三分之一的字节。&lt;/p&gt;
&lt;p&gt;顺便说，KV cache 的布局也是为带宽服务的：V 被转置成 &lt;code&gt;[d, cap]&lt;/code&gt;，按 256 token 一块存成 &lt;code&gt;[d, 256]&lt;/code&gt; 的连续布局。这样一来，P·V 每次流一个连续的 32 KB tile，走满 burst；不做这个，那 128 行里每一行都是相隔 cap 字节的一小块，带宽利用率会被打散。&lt;/p&gt;
&lt;h2 id=&#34;两个容易被忽略的工程选择&#34;&gt;两个容易被忽略的工程选择&lt;/h2&gt;
&lt;p&gt;既然 decode 卡在内存墙上，作者在「少搬字节」这件事上是下了功夫的，值得单独说说。&lt;/p&gt;
&lt;p&gt;4-bit 权重不是随便截断的。它用的是 FP4 值加上&lt;strong&gt;两级 block scale&lt;/strong&gt;，平均 4.25 bit 一个权重，而且专门把 LM head 留在 int8——因为最后一层直接决定输出 token，精度损失最敏感。作者在 &lt;code&gt;docs/quant.md&lt;/code&gt; 里如实记录了每个模型 perplexity 的代价，没有藏。换来的是什么？每 token 的字节数少约三分之一，decode 提速 40%（Qwen3.5）到 45%（Qwen3、LFM2）。对一个内存墙绑死的设计，这个交换划算得几乎不用犹豫。&lt;/p&gt;
&lt;p&gt;另一件事是 MoE。这块卡只有 4 GiB 逻辑 DRAM，怎么跑比卡内存还大的专家模型？&lt;/p&gt;
&lt;p&gt;作者的做法是这样的：卡自己负责每个 token 的路由，并计算&lt;strong&gt;每一个&lt;/strong&gt;专家；它把专家按层放在 DRAM 的 slot 里，host 只负责把缺失的专家从 pool 文件按 PCIe 的速率补进这些 slot。&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;模型&lt;/th&gt;
          &lt;th&gt;参数量/激活&lt;/th&gt;
          &lt;th&gt;decode&lt;/th&gt;
          &lt;th&gt;专家命中率&lt;/th&gt;
          &lt;th&gt;每 token 流式字节&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;LFM2.5-8B-A1B&lt;/td&gt;
          &lt;td&gt;8.5B / 1.7B&lt;/td&gt;
          &lt;td&gt;10.6 tok/s&lt;/td&gt;
          &lt;td&gt;98.5%&lt;/td&gt;
          &lt;td&gt;5.2 MB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Qwen3.5-35B-A3B&lt;/td&gt;
          &lt;td&gt;34.7B / 3.0B&lt;/td&gt;
          &lt;td&gt;3.95 tok/s&lt;/td&gt;
          &lt;td&gt;62%&lt;/td&gt;
          &lt;td&gt;153 MB @ 1.41 GB/s&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;同样是「能跑」，两者的体感差得很远。8B 那个命中率 98.5%、每 token 只流 5.2 MB，基本是在卡上跑；35B 那个命中率只有 62%、每 token 要流过 153 MB，PCIe 成了新的墙——它证明的是这套 offload 机制成立，而不是它好用。作者也没把它写成亮点，这一点挺诚实。&lt;/p&gt;
&lt;h2 id=&#34;硬件是怎么一晚上长出来的&#34;&gt;硬件是怎么一晚上「长」出来的&lt;/h2&gt;
&lt;p&gt;仓库里有一份 &lt;code&gt;docs/status.md&lt;/code&gt;，标题是「2026-09-24，第一次 Vivado 构建之前」。它记录了一夜的优化，我觉得这是整份文档里最能说明「AI 参与设计」到底改变了什么的证据。&lt;/p&gt;
&lt;p&gt;yosys 的综合估计（不含 XDMA 和 MIG IP），从「夜里开始」到「现在」：&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;指标&lt;/th&gt;
          &lt;th&gt;夜开始时&lt;/th&gt;
          &lt;th&gt;现在&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;LUT&lt;/td&gt;
          &lt;td&gt;131,915&lt;/td&gt;
          &lt;td&gt;82,224（xc7k480t 的 27.5%）&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;FF&lt;/td&gt;
          &lt;td&gt;55,890&lt;/td&gt;
          &lt;td&gt;35,944&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;DSP&lt;/td&gt;
          &lt;td&gt;416&lt;/td&gt;
          &lt;td&gt;267&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;BRAM36&lt;/td&gt;
          &lt;td&gt;603&lt;/td&gt;
          &lt;td&gt;603&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;逻辑延迟&lt;/td&gt;
          &lt;td&gt;14.94 ns&lt;/td&gt;
          &lt;td&gt;5.56 ns&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;估计 fmax&lt;/td&gt;
          &lt;td&gt;41 MHz&lt;/td&gt;
          &lt;td&gt;106 MHz&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;一晚上把估计 fmax 从 41 MHz 抬到 106 MHz，LUT 砍掉近四成。作者把这一夜拆成两部分：一部分是人工做的跨单元时序修复（TMEM arbiter 改成写掩码仲裁、grant 只到 BRAM enable、给 VPU/quantizer/MXU 累加路径的输入寄存 TMEM 数据、把串行的冲突计算改成并行的一对一比较……），另一部分是&lt;strong&gt;组件锦标赛&lt;/strong&gt;：让 Opus agent 一个一个单元去调，最终接受了 32 个胜出方案，每个单元估计 fmax 都过了 110 MHz，MXU 和 quantizer 还小了 40–60%。&lt;/p&gt;
&lt;p&gt;这里我有一点保留。&lt;code&gt;docs/status.md&lt;/code&gt; 里那句「人类仍然握着 merge 闸门」很关键——agent 能把局部单元调到更快更小，但「整颗芯片成不成」（跨单元时序、DRAM 校准、PCIe、复位极性）仍然是人在兜底。文档里甚至记了一个典型的人工介入案例：&lt;code&gt;bd.tcl&lt;/code&gt; 里的 &lt;code&gt;rst_core&lt;/code&gt; 把 MMCM 的 &lt;code&gt;locked&lt;/code&gt; 信号接到了 active-high 的复位输入上，这在硬件上会把核心一直摁在复位里。这种 bug 不是性能问题，是「根本起不来」，agent 的局部指标看不出它。修完之后，作者给 &lt;code&gt;make lint&lt;/code&gt; 加了一条规则，要求每个 &lt;code&gt;proc_sys_reset&lt;/code&gt; 都必须声明极性。这个细节比任何「AI 设计硬件」的口号都更有信息量。&lt;/p&gt;
&lt;h2 id=&#34;它到底给谁用&#34;&gt;它到底给谁用&lt;/h2&gt;
&lt;p&gt;读完一圈，我对它的定位有了判断。&lt;/p&gt;
&lt;p&gt;它&lt;strong&gt;不是&lt;/strong&gt; Nvidia 的替代品，也&lt;strong&gt;不是&lt;/strong&gt;要跟 Ascend、Zhenwu 这种量产芯片抢地盘。它更像一座显微镜加试验台：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果你想搞明白「一个 LLM 推理在硬件上究竟怎么跑」，&lt;code&gt;docs/&lt;/code&gt; 那条阅读路线（ISA → kernel/compiler → isasim → rtl → 整模型 → 板）是很少见的、可以从 Python 的 matmul 一路读到导线的完整路径；&lt;/li&gt;
&lt;li&gt;如果你关心「AI agent 到底能不能做硬件设计」，这里是目前少有的、把 agent 循环、门禁、奖励函数和真实板级验证都摆到台面上的公开实验；&lt;/li&gt;
&lt;li&gt;那个 bit-exact 的性质，让它变成一个可复现的参照系——你在模拟器上验证过的数值，能在真卡上逐 bit 重现，这在「AI 推理加速器」这个领域其实是很奢侈的事情。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;缺陷也很明确，我列一下，免得读者买错预期：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;板子是小众的（Inspur YPCB-00338），一个两百多 star 的项目，几乎只有作者一人在推，&lt;code&gt;pushed_at&lt;/code&gt; 一直在动却始终没有正式 release；&lt;/li&gt;
&lt;li&gt;它只跑小模型。我看到的实测最大到 Qwen3.5-4B，3.8B 的 Phi-4-mini 在 int8 下只有 3.99 tok/s。MoE 大模型（Qwen3.5-35B-A3B）能把专家流式 offload 跑起来，但只有 3.95 tok/s，62% 的专家命中率说明它是一种「能跑」而非「好用」的形态；&lt;/li&gt;
&lt;li&gt;名字撞车（前面提过），搜资料时要小心认错项目。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;写在最后&#34;&gt;写在最后&lt;/h2&gt;
&lt;p&gt;2026 年，「AI 自己写代码」已经不稀奇了。这个仓库让我觉得值得写一篇，是因为它把 AI 生成的范围往前推了一格——从软件推到了可以直接烧进 FPGA 的硬件。&lt;/p&gt;
&lt;p&gt;但更有意思的其实是它顺手暴露的另一件事：当 AI 的效率被推到硬件层，约束并没有消失，只是换了张脸。openTPU 里最扎眼的数字不是 85.8 tok/s，也不是 106 MHz，而是 decode 时那 82–94% 的 DRAM 占用率。算力可以靠 agent 一晚上翻倍，内存墙一步都动不了。&lt;/p&gt;
&lt;p&gt;所以真正稀缺的，从来不是「设计得多快」，而是「知道墙在哪」。这一点，agent 目前还答不上来。&lt;/p&gt;
&lt;h2 id=&#34;相关阅读&#34;&gt;相关阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20261001-deepseek-huawei-ascend-tilelang/&#34; &gt;《DeepSeek 开源华为 Ascend 编程工具链！TileLang 剑指 CUDA，中国 AI 芯片生态加速独立！》&lt;/a&gt; —— 同样在把 LLM 往自研硬件上拉，但走的是编程工具链的路，不是 RTL。&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20260925-alibaba-zhenwu-v900-ai-chip/&#34; &gt;《豪掷 $530 亿！阿里发布最强 AI 芯片 Zhenwu V900…》&lt;/a&gt; —— 量产 AI 芯片长什么样，可以拿来和这块「学习用」的 FPGA 对照。&lt;/li&gt;
&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://www.yesmiracle.net/post/20260927-deepseek-dsec-sandbox-infrastructure/&#34; &gt;《三百万沙箱一天！DeepSeek 开源弹性计算平台 DSec…》&lt;/a&gt; —— 另一条「AI 推理与训练」的底层基建路线，在软件侧。&lt;/li&gt;
&lt;/ul&gt;
</description>
        </item>
        
    </channel>
</rss>
