Tenstorrent 這九天終於走完了。從 Tenstorrent 硬體架構開始複習,接著看 TT-Metal 程式模型和 TT-MLIR compiler stack,再比較 Triton 到不同 NPU 的 lowering flow。最後用 TileLoom paper 和 matmul IR 把 Triton on Tenstorrent 的路線走一次。

Day21 從 WormholeB0 README 開始。 Wormhole ASIC 由多種 tile 組成
Tensix tile 負責 compute,DRAM tile 是 GDDR6 的入口,Ethernet tile 連線到其他 ASIC,PCIe tile 接 host,ARC tile 管理晶片與板卡。Wormhole 有兩套方向相反的 NoC,把這些節點接成 2D torus。
這裡的 tile 有兩種意思
32×32 elements。一顆 Tensix hardware tile 會反覆處理許多 tensor tiles。Wormhole 的五種 hardware tile 分工也不一樣
| Hardware tile | 數量與角色 | 讀程式時要找什麼 |
|---|---|---|
| Tensix | 80 個實體 tile;產品實際開放數量可能不同 | 本地 L1、data movement core、Unpack/Math/Pack、Matrix/Vector/Scalar Unit |
| DRAM | 18 個 NoC 入口,共同連到 12 GiB GDDR6 | DRAM bank、來源座標、DRAM → L1 搬移 |
| Ethernet | 16 個 100 GbE 節點 | 晶片內 NoC 如何連線到多 ASIC fabric |
| PCIe | 1 個 PCIe 4.0 x16 host interface | Host command 與 host-to-device traffic 從哪裡進入 |
| ARC | 1 個管理節點 | 初始化、監控與板卡管理;不執行使用者 kernel |
Tensix tile 裡還可以再拆。Wormhole 每顆 Tensix 有 1464 KiB 本地 L1、五個 Baby RISC-V core、兩個 unpacker、Matrix Unit、Vector Unit、Scalar Unit 與四個 packer。Baby RISC-V core 負責控制資料搬移及派送工作,主要矩陣與向量吞吐量來自 Tensix coprocessor。
所以看 NoC layout 時,先認節點種類,再沿著來源與目的座標追資料,tensor tile 從哪個 DRAM tile 出發、進到哪顆 Tensix 的 L1、是否送給一列或一欄 cores,最後又從哪裡寫回。這些問題會變成 reader/writer kernel,在 TileLoom 則變成 placement、broadcast 與 reuse 規劃。

這天先介紹 Tenstorrent software stack
TT-Forge / TT-NN / TT-Lang
↓
TT-Metalium / TT-Metal
↓
TT-LLK / Tensix instruction level
接著看 TT-Metal / Metalium,重要的三種 kernel: reader kernel、compute kernel、writer kernel
Reader 把資料從 DRAM 搬進 circular buffer。Compute 等 buffer 有資料,做 tile operation。Writer 等 output buffer 有結果,再寫回目標位置。
Circular buffer 同時是 producer-consumer queue 和同步契約。cb_reserve_back、cb_push_back、cb_wait_front、cb_pop_front 這些 API 會把資料流的正確性建立起來。
看這張圖時,可以跟著一個 tensor tile 走。Reader 先對 input CB 保留空間,發出非同步 NoC read,等 barrier 確認傳輸完成後才 push;compute 等到 input CB 有資料後執行 tile operation,再把結果 pack 到 output CB;writer 最後等 output tile 就緒,經 NoC 寫回 DRAM。
Host 與 device 兩側也要分開。Host 建立 buffer、circular buffer、kernel 與 program,設定 runtime arguments,再將 program 排進 command queue。Reader、compute、writer 是 device kernels,使用 L1 裡的 circular buffer 狀態協調進度。
檢查可以核對 producer 與 consumer:每個 reserve 後面有沒有 push,每個 wait 後面有沒有 pop,非同步 NoC operation 是否在公布資料前完成。少掉其中一步,程式可能卡住,也可能讓 consumer 讀到尚未完成的資料。

TT-MLIR 建在 MLIR 之上,定義 Tenstorrent 專用的 dialect 和 transformation pass。目前文件展示 TTIR 可分流到 TTNN runtime pipeline,也有 D2M、TTKernel 與 TTMetal 這條更低階路徑。這個版本的 project-structure 文件仍將 TTMetal runtime namespace 標示為 not implemented,FlatBuffers 文件也將 .ttb 標示為 unsupported。
這層把前一天看到的 TT-Metal 程式模型,接到更上層的 model 和 compiler frontend
front-end dialect / model
-> TT-MLIR TTIR
├─> TTNN dialect
│ -> TTNN FlatBuffer / runtime
└─> D2M
-> TTKernel + TTMetal dialect
每個 dialect 保存的資訊不同。ttir 還像 tensor compute graph;ttnn 加入 physical tile、memory space、layout 與 tensor lifetime,d2m 展開 grid、iterator、circular buffer 與 data movement;ttkernel 出現 reader/compute/writer 的同步和 tile instruction,ttmetal 則描述 host buffer、kernel configuration 與 program dispatch。
因此讀 lowering 不只看 operation 名稱有沒有改。更值得問的是 tensor shape 是否維持、logical layout 何時變成 physical layout、buffer lifetime 在哪一層出現,以及 device kernel 與 host program 是從哪個 pass 開始分開。這些資訊能幫忙判斷錯誤應該停在高階 tensor 語意、memory layout,還是 runtime 邊界。
這裡也保留版本限制。TTNN FlatBuffer/runtime 路徑與較低階的 D2M 路徑需要分別核對公開 artifact 與支援狀態,本文核對的 TT-MLIR 文件仍將 .ttb 與部分 TTMetal runtime support 標示為未完成。

把六條 flow 放在一起看
Triton-Tenstorrent TTMLIR
TileLoom
Triton-Ascend
Triton-XDNA
Hexagon-MLIR
Triton-MTIA
有些 flow 保留 TTIR / TTGIR 後接硬體 dialect,有些 flow 經由 triton-shared 轉成 affine / linalg / scf / memref,再進 backend-specific compiler。
TileLoom 屬於後者
Triton
-> TTIR
-> triton-shared
-> affine / linalg / scf / memref
-> TileLoom dataflow-aware MLIR
-> TT-Metalium C API
這條線很適合研究,因為每個階段都有 IR 可以看
比較六條 flow 時,可以先找 bridge 插入的位置。triton-shared 會把可辨識的 Triton block 與 pointer pattern 轉成 affine、linalg、scf、memref 等通用 MLIR,後端再補上各自的 core array、local memory、DMA/NoC、vector 或 matrix unit 資訊。
讀一條新的 NPU flow 時,我會依序找四種 artifact,Triton dialect 在哪裡結束、通用 MLIR 保留哪些存取關係、硬體 dialect 何時加入 placement 與 memory space,以及最後由哪個 runtime 載入與啟動。若 pointer analysis 只能處理 strided/affine pattern,原本合法的 Triton kernel 也未必能走完這條 lowering。

TileLoom paper 的問題設定是 spatial dataflow accelerator 的 programmability。Tile language 已能描述單一 tile 的運算,整張 logical grid 如何放到有限的 physical cores,仍需要規劃 placement、execution waves、reuse 與 communication。
這類硬體有 local memory、NoC 與 core grid,可以減少 DRAM 往返。效能會受到幾個互相牽動的決策影響
TileLoom 接收 tile kernel 與 architecture representation,產生多個 dataflow candidates。這天的複習重點是 compiler 缺口:單一 tile 的 compute lowering,還不足以解決整張 spatial core grid 的資料流。
用 matmul 想最容易。C[m,n] 的 logical grid 可能大於實體 8×8 core mesh,所以同一批 cores 要分成多個 temporal waves。每個 candidate 都要回答 (m,n,k) 哪些軸放到 core grid、waves 用什麼順序執行、A/B tiles 是否能跨 core 共用,以及 L1 是否裝得下需要保留的資料。
TileLoom 的輸入因此有兩部分:tile program 提供運算與 affine memory access,architecture representation 提供 core、memory、interconnect、capacity、bandwidth 與 compute resource。輸出的 dataflow plan 包含 kernel body、placement、execution order、buffer 與 communication 決策。
如果只驗證單顆 core 上的 matmul 能算對,仍沒有回答整張 logical grid 是否會讓部分 cores 閒置、讓多筆 traffic 擠在同一條 NoC link,或重複從 DRAM 載入原本可共用的 tile。

Day26 把 TileLoom 的 planning pipeline 拆開
Spatiotemporal mapping
→ Reuse analysis
→ Data movement planning
→ Performance model
→ Top-k candidates
→ optional hardware profiling
Spatial mapping 決定 logical axis 如何對應 core grid;temporal mapping 安排同一組 cores 先後處理哪些 waves。Reuse analysis 從 affine access dependency 判斷 spatial/temporal reuse,再將機會落成 broadcast、load hoisting 與 buffer lifetime。Performance model 把 compute、memory 與 NoC cost 放在一起估計,用來排序 candidates。
Paper 的實驗也提醒我讀數字要連同條件一起看。FlashAttention 有明顯 reuse,Flash Decode 的 query length = 1 讓可用 parallelism 變少;GEMM 接近 vendor library;Mamba Chunk Scan 的 baseline 是 unfused implementation。模型的用途是 candidate ranking,論文也保留 optional profiling 處理錯排,不能把估計值當成 cycle-accurate simulation。
Reuse analysis 的判斷可以直接從 index dependency 讀。某個 access 不依賴 spatial index,表示沿該軸的 cores 會使用相同 tile,可能轉成 NoC broadcast;不依賴 temporal loop variable,表示不同 waves 可重用同一份資料,load 可以 hoist,但 buffer lifetime 也會變長。
Performance model 會估計 compute、DRAM、local memory 與 NoC cost,也要考慮 load/compute/store 能否重疊。它的工作是縮小候選集合,之後可對 top-k 做硬體 profiling。這和「模型精確預測每一個 cycle」是不同的驗證目標。

Day27 開始閱讀舊 loom-dataflow standalone pipeline 的 committed artifact
00 frontend
affine.parallel + scf.for + linalg.matmul
01 tensor canonicalized
統一 destination 與 Loom bufferization bridge
02 explicit memory access
加入 L1 allocation、DRAM/L1 copy 與 semaphore
這三步先保留 matmul 的 M/N/K 與計算語意,再讓 buffer、copy 與同步操作成為 IR 的一部分。比較前後 IR 時要核對 shape、offset 與 load/store 方向,避免 memory rewrite 改壞原始計算。
00_from_helion_frontend.mlir 先找 affine.parallel、K loop 與 linalg.matmul;01_tensor_canonicalized.mlir 檢查 destination handoff 與 proxy copy;02_explicit_memory_access.mlir 再找 loom.alloc、loom.semaphore_take/give、DRAM ↔ L1 的 loom.copy。
SSA 編號變動本身沒有獨立意義。這裡要關注 operation 的 operand 來源、tensor/memref shape、subview offset、memory space、copy 方向與 buffer lifetime。尤其 semaphore_take/give 在這層描述資源生命週期,還不能直接解讀成最終硬體 semaphore 指令。

03 hardware mapping
logical loops → 8×8 mesh,展開 16 個 mapping candidates
04 reuse analysis
檢查 subview access 不依賴哪些 spatial/temporal indices
05 enumerate broadcast
loom.copy 開始帶有 area 與 region
A[m,k] 不依賴 n,B[k,n] 不依賴 m,所以兩者可能沿不同 mesh 方向共用。Step 6 再從 05 產生 staged ETG constraint JSON。
03_hardware_mapping.mlir 會把 logical loops 放到 8×8 mesh;這組 dump 展開 16 個 mapping candidates。數量增加代表列舉不同 spatial/temporal 對應,尚未代表 solver 已選出最佳方案。
到了 04_reuse_analysis.mlir,重點是 subview 的 affine offset 依賴哪些 induction variables。05_enumerate_broadcast.mlir 再把分析結果寫回 loom.copy:area : [8, 1] 描述接收區域大小,UL/LR 則給出該區域在 mesh 上的座標範圍。這讓A 沿一軸共用、B 沿另一軸共用成為可檢查的 IR attribute。
Step 6 產生 staged ETG,供 constraint model 使用,裡面保存 task、dependency、symbol 與限制條件。Program MLIR 不會因為產生 JSON 就自動取得具體 block sizes;要看到 resolved ETG 或 solver assignment,才能往下聲稱候選已被求解。
Day27~Day29 使用的 2026-07-31 舊 run_pipeline.sh 到 Step 6 只輸出 staged ETG,沒有接著執行 MLAR resolution 或 CP-SAT。這組舊 artifact 不能用來證明 solver 已選出 block sizes。

06 materialize + canonicalize + BridgeToOSB
symbol → fixed shape
loom.subview → memref.reinterpret_cast
07 One-Shot Bufferization
tensor SSA dataflow → memref in-place dataflow
08 TT opt
zero-filled linalg.matmul pattern → loom.matmul
舊 Step 7 沒有讀取 resolved ETG 或 solver assignment,而是觸發 {64,64,512} placeholder。當中的 tile_k=512 超過原始範例 K=256,constraint JSON 的 problem size 也和 00 對不上。這組 artifact 適合學習 pass 形狀,不能視為已由 CP-SAT 求解並驗證可安全執行的設定。
final.mlir 是獨立硬體模型,與 08_tt-opt.mlir 沒有 lowering 關係。08 也尚未展示 TTKernel/TT-Metal code、host launch 或裝置數值驗證,因此應稱為 backend-oriented Loom IR。
Materialize 的工作是把 tile symbol 代入具體整數,接著 canonicalizer 折疊常數、SymbolDCE 清掉不再使用的 symbol,BridgeToOSB 則把多維 subview offset 線性化成 memref.reinterpret_cast 可以表達的形式。這一步一旦代入錯誤尺寸,後面的 bufferization 仍可能產生形式上完整的 IR,所以 tile bound 必須在這裡另外檢查。
One-Shot Bufferization 將 tensor SSA dataflow 轉成 memref in-place dataflow。閱讀 06 → 07 時應追 alias、讀寫對象與 buffer lifetime,不能用檔案行數變少推論 candidate selection。08 的 TT opt 再辨認 zero-filled linalg.matmul pattern,改寫成 loom.matmul,先前的 copy 與 semaphore operations 仍留在周圍。
如果要稱為端到端執行證據,後面至少還要看到實際 TT-Metal kernel source、circular buffer configuration、NoC command、kernel binary、host launch,以及裝置上的數值與效能結果。這組舊 dump 沒有提供這些 artifact。
截至本文核對的新版 oom@f65d199b,monorepo 的完整 run_pipeline() 才會串接 exploration、MLAR resolution、CP-SAT 與 materialization。
回顧這三十天,CPU、GPU、Tenstorrent 都在執行 tensor operator。差別不只在核心數量,也在硬體把哪些資源與決策暴露給 compiler。
| 問題 | RISC-V CPU | NVIDIA GPU | Tenstorrent ASIC |
|---|---|---|---|
| 工作如何分割 | loop iteration、thread、RVV vector | grid、CTA、warp、lane | logical tile、Tensix core coordinate、temporal wave |
| 鄰近計算的儲存 | register、L1/L2 cache | register、shared memory、L1/L2 | 每顆 Tensix core 的 L1 |
| 矩陣運算 | scalar/RVV kernel | Tensor Core MMA | Tensix Matrix Unit |
| 資料搬移 | load/store、cache hierarchy | global ↔ shared、coalescing、async copy/TMA | DRAM ↔ L1、L1 ↔ L1、NoC broadcast |
| 常見效能限制 | vector utilization、cache miss、memory bandwidth | occupancy、register/shared-memory pressure、layout conversion、HBM traffic | L1 capacity、core utilization、NoC traffic、placement |
| 本系列 compiler 路徑 | PyTorch → Inductor → C++/RVV | Triton → TTIR/TTGIR → LLVM/PTX/SASS | Triton/Helion → TileLoom MLIR → mapping/reuse/broadcast |
CPU 版本最常從巢狀 loop 開始
M/N/K blocking
→ strip-mining
→ vsetvl
→ RVV load / multiply-accumulate / store
TorchInductor RVV 的工作,是讓一般 F.linear 經過 graph lowering、shape gate 與 codegen,走到合適的 RVV microkernel。Compiler 主要處理 loop、vector width、register use、packed weight 與 cache-friendly layout。
Triton matmul 先描述某個 program instance 負責的 output tile,NVIDIA backend 再決定
program id
→ CTA tile
→ warp/lane layout
→ global memory pointer
→ shared-memory staging
→ MMA instruction
Day14~Day19 實際看到這條線的證據
Python specialization
→ TTIR:tile-level 計算與 pointer/mask
→ TTGIR:CTA/warp/lane layout、shared memory、MMA
→ LLVM IR/NVVM:address space、intrinsic
→ PTX:virtual ISA
→ cubin/SASS:實際機器碼
→ CUDA Driver API:launch
GPU compiler 不只做大量 thread 平行化,它還要選 shared-memory layout、安排 async copy 或 TMA、插入 barrier,並在 register、shared memory 與 occupancy 之間取捨。
Tenstorrent 接續 tile-based program,新增 spatial dataflow 的問題
logical M/N/K tile
→ Tensix (x, y) placement
→ DRAM/L1 buffer
→ NoC0/NoC1、傳輸端點與 broadcast region
→ per-core compute
→ writeback
A tile 若被同一列 output core 共用,可以沿一個方向 broadcast,B tile 則可能沿另一個方向傳送。TileLoom 會列舉 mapping、分析 subview offset dependency,再把 reuse 改寫成 loom.copy 的 area 與 region。
兩者都大量使用 tile,也都努力讓資料停留在靠近運算單元的記憶體。GPU 的 kernel 會控制 CTA 內 shared memory、warp layout 與 pipeline,CTA 到 SM 的派發則由 GPU scheduler 處理。Tenstorrent 讓程式更直接地面對 Tensix core 的 (x, y) 座標、各 core 的 L1,以及跨 core NoC 傳輸。
用 compiler IR 來看,它們要求 compiler 規劃的座標系與資料搬移介面不同。
NVIDIA TTGIR:
tensor element 屬於哪個 CTA / warp / lane?
shared-memory encoding 和 MMA encoding 是什麼?
TileLoom IR:
logical loop 映射到哪個 physical dimension?
copy 的接收 area / region 是什麼?
subview 有沒有 spatial / temporal reuse?
謝謝大家看到這裡,下一篇會是最後的結尾。