PTO-ISA TPOP 指令详解:从 TPipe FIFO 弹出消费者 Tile 的 Cube-Vector 数据通路编程
2026/9/20 19:58:24 网站建设 项目流程

PTO-ISA TPOP 指令详解:从 TPipe FIFO 弹出消费者 Tile 的 Cube-Vector 数据通路编程

【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa

TPOP 是 PTO(Parallel Tile Operation)虚拟指令集架构中用于Cube-Vector(CV)跨核通信的关键消费者侧指令:它从TPipe环形 FIFO 中弹出生产者写入的 tile,并在等待数据就绪同步后将其交给消费者计算单元。本文以 docs/isa/TPOP.md 为骨架,结合 include/pto/common/pto_instr.hpp 中的 intrinsic 声明、include/pto/cpu/TPop.hpp 的 CPU_SIM 实现以及 NPU 各后端TPush.hpp源码,系统讲解 TPOP 的操作语义、重载形式、切分模式、约束条件与实战用法。读完本文,你将掌握 C2V/V2C 场景下 TPOP 的完整编程套路,能够结合TPUSHTFREETALLOC编写正确的 CV FIFO 数据流。

TPOP 在 CV FIFO 通信中的位置

在 PTO 的 Cube-Vector 流水线通信模型里,TPipe是一个带环形槽位(Ring FIFO)的跨核 FIFO,生产者与消费者通过三条指令协同:

  • TPUSH(Pipe&, TileData&, Split):生产者把 tile 存入当前 FIFO 槽位,并为消费者记录数据就绪同步;生产者索引在槽位地址计算完成后递增。
  • TPOP(Pipe&, TileData&, Split):消费者等待生产者的数据就绪同步,把当前 FIFO 槽位加载进消费者 tile;消费者索引在槽位地址计算完成后递增。
  • TFREE(Pipe&, Split):释放 FIFO 槽位空间,供生产者复用。

TPOP是这条链路中唯一由消费者侧执行的"取数"动作,其指令名即 "Tile Pop" 的缩写。它支持两类数据视图:

  1. TileData 流程(本地 tile 缓冲):TPOP把槽位数据加载进本地Tile(向量 tile 或矩阵 tile),随后消费者可直接对 tile 计算;
  2. GlobalData 流程(全局内存槽位视图):TPOP只把GlobalTensor视图绑定到槽位基地址,不做数据搬运,消费者用TLOAD等指令按需读取。

操作语义:TileData 与 GlobalData 两条数据流

TileData 流程(本地缓冲)

对于TPOP(Pipe&, TileData&, Split),完整的生命周期是"生产者 push → 消费者 pop → 空间释放"三步:

  1. TPUSH(Pipe&, TileData&, Split)将生产者 tile 存入当前 FIFO 槽位并记录数据就绪同步;生产者 tile 索引在槽位地址计算完成后递增。
  2. TPOP(Pipe&, TileData&, Split)等待数据就绪同步,将当前 FIFO 槽位加载到消费者 tile;消费者 tile 索引在槽位地址计算完成后递增。每次TPOP都会执行一次数据就绪等待。
  3. TFREE(Pipe&, Split)释放 FIFO 槽位空间。需要注意平台差异:A2A3 平台上该接口为空操作(no-op),因为TPOP已在内部执行空闲空间通知;A5 平台上TFREE才会真正释放TPOP使用的 FIFO 槽位空间。

这条平台差异可以直接从源码中得到印证:A2A3 的TPipe与 A5 的TPipe都定义了shouldWaitFree(tileIndex)SyncPeriod,但 A5 后端由于引入了显式的本地 FIFO 槽位管理(LocalSlotNum),需要TFREE配合归还槽位。

GlobalData 流程(GM 槽位视图)

当通信双方不希望把数据搬进本地 tile,而是直接在全局内存槽位上读写时,使用GlobalData重载。其完整生命周期为:

  1. TALLOC(Pipe&, GlobalData&)TPipe分配一个生产者 FIFO 槽位,并暴露为GlobalTensor视图;生产者可通过TSTORE等指令向该槽位写入数据。
  2. TPUSH(Pipe&, GlobalData&)为已由TALLOC分配的槽位记录数据就绪同步,把该槽位提交给消费者。注意:它本身不存储 tile 数据
  3. TPOP(Pipe&, GlobalData&)等待数据就绪,把gmTensor赋值为当前 FIFO 槽位地址,并递增消费者 tile 索引。它不把数据加载进本地 tile,也不释放槽位;消费者可通过TLOAD等指令从槽位读取数据。
  4. TFREE(Pipe&, GlobalData&)释放由TPOP(Pipe&, GlobalData&)返回的 FIFO 槽位视图,通知生产者该槽位空间已空闲。

GlobalData 流程的价值在于:生产者与消费者之间的数据始终驻留全局内存(GM),TPOP只做地址赋值与同步,避免了一次无谓的本地缓冲拷贝,适合大 tile 或需要多消费者共享数据的场景。注意 CPU_SIM 目前不支持 GlobalData 重载,该流程仅在 NPU 后端可用。

C++ Intrinsic 声明与重载形式

TPOP的 C++ intrinsic 声明位于 include/pto/common/pto_instr.hpp,共四个重载:

// 重载 1:TileData,显式指定 TileSplitAxis template <typename Pipe, typename TileCons, TileSplitAxis Split, std::enable_if_t<is_tile_data_v<TileCons>, int> = 0, typename... WaitEvents> PTO_INST RecordEvent TPOP(Pipe &pipe, TileCons &tile, WaitEvents &... events); // 重载 2:TileData,显式指定 subblock ID(按核切分场景) template <typename Pipe, typename TileCons, TileSplitAxis Split, typename... WaitEvents> PTO_INST RecordEvent TPOP(Pipe &pipe, TileCons &tile, int32_t subBlockId, WaitEvents &... events); // 重载 3:参数反转、不指定 Split(等效 TILE_NO_SPLIT) template <typename TileData, typename Pipe, typename... WaitEvents> PTO_INST RecordEvent TPOP(TileData &tile, Pipe &pipe, WaitEvents &... events); // 重载 4:GlobalData 槽位视图 template <typename Pipe, typename GlobalData, TileSplitAxis Split, std::enable_if_t<is_global_data_v<GlobalData>, int> = 0, typename... WaitEvents> PTO_INST RecordEvent TPOP(Pipe &pipe, GlobalData &gmTensor, WaitEvents &... events);

从 include/pto/common/pto_instr.hpp 的实现可以看到,每个重载都先调用detail::PtoWaitEvents(events...)处理等待事件,再转发到对应后端的TPOP_IMPL;参数反转重载在 CPU 实现中直接转发为TPOP_IMPL<Pipe, TileCons, TileSplitAxis::TILE_NO_SPLIT>(见 include/pto/cpu/TPop.hpp),即TPOP(dst, pipe)等价于不切分的标准写法。所有重载返回RecordEvent,可用于与异步事件链组合。

其中WaitEvents变参允许在 pop 前插入事件等待(例如等待上一次TLOAD完成),便于在流水线中做依赖管理。

TPipe 模板参数详解

Pipe通常是各后端TPush.hpp中声明的TPipe模板类,三端声明完全一致,分别是 include/pto/cpu/TPush.hpp、include/pto/npu/a2a3/TPush.hpp、include/pto/npu/a5/TPush.hpp:

template <uint8_t FlagID, uint8_t DirType, uint32_t SlotSize, uint32_t SlotNum, uint32_t LocalSlotNum = 2, bool IsNoSplit = false, bool EN_UNIT_FLAG = false> struct TPipe;

各参数含义与取值要点:

参数含义说明
FlagID同步标志 ID用于 CV 核间同步,FlagID + 1 <= MAX_SYC_ID(15)DIR_BOTH场景下硬件限制要求FlagID + 1 < 15
DirType通信方向Direction::DIR_C2V(1)、DIR_V2C(2)、DIR_BOTH(3)、DIR_V2C_CTRL(4);A5 额外支持DIR_C2V_GM(5)、DIR_V2C_GM(6)、DIR_BOTH_GM(7)
SlotSize单个 GM 槽位字节数决定 ring 中每个槽位可承载的数据量
SlotNumGM 槽位个数决定 FIFO 深度与SyncPeriod
LocalSlotNum本地 FIFO 槽位数默认 2,用于本地缓冲轮转
IsNoSplit是否不切分模式true时配合TILE_NO_SPLIT使用
EN_UNIT_FLAG是否启用单元标志默认false

DirectionTileSplitAxis枚举定义在 include/pto/common/fifo.hpp。TPipe内部通过DIR_MASK & DirType提取方向位,并以此派生is_c2vis_v2cis_both等编译期常量;A2A3 的 TPush.hpp 中static_assert只允许 C2V/V2C/Both/V2C_CTRL 四种方向,而 A5 的 TPush.hpp 额外支持 GM 系方向(C2V_GM、V2C_GM、BOTH_GM)。

TPipe内部通过RingFIFO<SlotSize, SlotNum, LocalSlotNum>(定义于 include/pto/common/fifo.hpp)组织槽位:GM_SLOT_BUFFER指向全局内存槽位基址,C2V_CONSUMER_BUF是向量侧(UB)消费者缓冲基址,V2C_CONSUMER_BUF是矩阵侧(L1)消费者缓冲基址,V2C_CONTROL_BUF是标量控制信号缓冲。

约束条件与切分行为

方向与消费者类型约束

  • TileData 消费者TileCons::Loc必须是TileType::VecTileType::MatTileType::Ctrl
  • 方向语义
    • Direction::DIR_C2V:向量核消费 Cube 核生产的数据(Cube 是生产者,Vector 是消费者);
    • Direction::DIR_V2C:Cube 核消费向量核生产的数据(Vector 是生产者,Cube 是消费者);
    • Direction::DIR_BOTH:同一 pipe 类型同时支持 C2V 与 V2C 两个方向的消费者。
  • 本地消费者缓冲区:C2V 的 vector 消费者,TPipe把 tile 分配到C2V_CONSUMER_BUF并做本地 FIFO 轮转;V2C 的 matrix 消费者,tile 被分配到V2C_CONSUMER_BUF并做本地 FIFO 轮转。

切分模式(TileSplitAxis)

TileSplitAxis枚举完整定义于 include/pto/common/fifo.hpp:

枚举值数值语义
TILE_NO_SPLIT01:1 模式不切分,仅使用 AIV0;A2A3 上要求 AIV0/AIV1 参与陪跑核间同步
TILE_UP_DOWN1沿行方向切分:AIV0 取上半区、AIV1 取下半区,行数必须为偶数
TILE_LEFT_RIGHT2沿列方向切分:AIV0 取左半区、AIV1 取右半区,列数必须为偶数
TILE_UP_DOWN_ODD3沿行方向奇行切分:AIV0 取 rows/2 + 1、AIV1 取 rows/2,行数必须为奇数
TILE_LEFT_RIGHT_ODD4沿列方向奇列切分:AIV0 取 cols/2 + 1、AIV1 取 cols/2,列数必须为奇数

文档聚焦的三种切分语义为:

  • TILE_NO_SPLIT:不施加子向量偏移。在 A2A3 上此模式需要 AIV0/AIV1 参与陪跑核间同步操作;
  • TILE_UP_DOWN:vector 子块消费上、下两个行半区;
  • TILE_LEFT_RIGHT:vector 子块消费左、右两个列半区。

NPU 同步模型

  • 数据就绪等待:每次TPOP都会执行一次数据就绪等待,即"每次 pop 必等一次 producer 就绪";
  • 空闲空间通知:空闲空间通知是稀疏的,由Pipe::SyncPeriod控制。SyncPeriod的推导规则在 include/pto/npu/a2a3/TPush.hpp 与 include/pto/npu/a5/TPush.hpp 中一致:(SlotNum <= 2) ? SlotNum : SlotNum / 2,即深 FIFO 时按一半槽位数周期性发空闲通知。

GlobalData 槽位视图约束

  • gmTensor被赋值为pipe.cons.tileIndex选择的 FIFO 槽位基地址;
  • 对于 C2V 切分模式,会按照Split施加向量子块偏移;
  • 从槽位视图完成所有加载之后,调用者必须调用TFREE(Pipe&, GlobalData&)归还槽位,否则生产者将无法复用该槽位。

CPU_SIM FIFO 模型约束

CPU_SIM 是 PTO 在 CPU 上的全功能模拟后端,其TPOP实现位于 include/pto/cpu/TPop.hpp。关键约束:

  • TPOP使用主机 FIFO 的互斥锁与条件变量等待生产者提交槽位;
  • TileData 数据从主机 FIFO 状态拥有的存储加载;即使TPipe携带非空 NPU GM workspace,CPU_SIM 的 TileData 流程也不会访问该 workspace(GlobalData 重载当前在 CPU_SIM 中不可用);
  • 切分模式根据当前 subblock 上下文选择 lane。对于启用IsNoSplit的 C2V 管道,TILE_NO_SPLIT会根据运行时 subblock 数量协调一个或两个 vector 消费者 subblock;其他 no-split vector 场景中,非参与 lane 按需填零;
  • 显式传入int32_t subBlockId的重载在 CPU_SIM 中未实现,应通过模拟 subblock 执行上下文选择 split lane;
  • DIR_BOTH下,消费者在其方向的独立环形队列中等待;no-split 与 V2C 消费者按生产者提交序号取出最早已提交槽位,在延迟释放造成环内空隙时仍保持 FIFO 顺序;重叠的 pop 会预留不同槽位,TFREE释放调用方消费者对应方向最早未释放的 pop;
  • 每次启用的TFREE在 CPU_SIM 中都会释放一个具体占用的槽位,不模拟NPU 的SyncPeriod通知节奏;
  • TileData 数据按消费者 tile 的声明形状读回,因此该形状必须与生产者推送的窗口一致TILE_NO_SPLIT下即其有效形状);
  • CPU_SIM 按逻辑坐标把 TileData 复制到目标布局;A5 本地 FIFO 的TPOP则是把 tile 绑定到 FIFO 地址、不做布局转换。要使 V2C 行为与 NPU 一致,应使用匹配的生产者与消费者布局(如 NZ Vec 到 NZ Mat),并在TPUSH前完成所需布局转换。

从 include/pto/cpu/TPop.hpp 的实现可以看到 CPU_SIM 的完整 pop 路径:首先通过ShouldNoSplitC2VConsumerLaneParticipate判断当前 lane 是否参与(双 lane C2V 协议激活时第二个 vector lane 才参与 pop;单 subblock 时走 inactive-lane 路径——FillTile填零并直接返回,不执行 pop,管道退化为单消费者);随后若pipe.cons.getWaitStatus()为真则调用wait<TileCons, Split>()等待数据;最后pipe.cons.pop<TileCons, Split>(pipe.fifo, tile)完成地址计算、TASSIGN与数据传输。

GlobalData 的 CPU_SIM 实现(include/pto/cpu/TPop.hpp)展示了槽位寻址的通用公式:slotIndex = pipe.cons.getTileId() % SLOT_NUMentryBase = slotIndex * SLOT_SIZE + pipe.cons.entryOffset,再把GM_SLOT_BUFFER + entryBase通过TASSIGN_IMPL赋给gmTensor

实战示例

以下三个示例完整继承自 docs/isa/TPOP.md,分别覆盖 C2V 向量消费、V2C 矩阵消费与 GlobalData 槽位弹出,可直接作为手写 CV FIFO 内核的模板。

C2V Vector Pop:向量核消费 Cube 产出的数据

#include <pto/pto-inst.hpp> using namespace pto; template <typename T> AICORE void example_c2v(__gm__ void *fifoMem) { constexpr uint32_t M = 128; constexpr uint32_t N = 128; constexpr uint32_t FlagID = 0; constexpr uint32_t FifoDepth = 2; constexpr uint32_t LocalBase = 0x0; using Pipe = TPipe<FlagID, Direction::DIR_C2V, M * N * sizeof(T), FifoDepth>; using VecTile = Tile<TileType::Vec, T, M / 2, N, BLayout::RowMajor, M / 2, N>; Pipe pipe(fifoMem, LocalBase, 0x0); // C2V:第二参数为 C2V 消费者缓冲基址,第三参数为 V2C 消费者缓冲基址(此处为 0) VecTile tile; TPOP<Pipe, VecTile, TileSplitAxis::TILE_UP_DOWN>(pipe, tile); }

要点:TPipe构造参数依次为(GM_SLOT_BUFFER, C2V_CONSUMER_BUF, V2C_CONSUMER_BUF),对应 include/pto/common/fifo.hpp 中RingFIFO构造函数。C2V 场景下第二参数传入向量侧消费者缓冲基址LocalBase,第三参数置 0。VecTile声明为M/2 x N的行主序 tile,与TILE_UP_DOWN的上半区尺寸匹配——即消费者 tile 的有效形状必须与生产者推送窗口一致(CPU_SIM 会按声明形状读回数据)。

V2C Matrix Pop:Cube 核消费向量产出的数据

#include <pto/pto-inst.hpp> using namespace pto; template <typename T> AICORE void example_v2c(__gm__ void *fifoMem) { constexpr uint32_t M = 128; constexpr uint32_t N = 128; constexpr uint32_t FlagID = 0; constexpr uint32_t FifoDepth = 2; constexpr uint32_t LocalBase = 0x0; using Pipe = TPipe<FlagID, Direction::DIR_V2C, M * N * sizeof(T), FifoDepth>; using MatTile = Tile<TileType::Mat, T, M, N, BLayout::ColMajor, M, N, SLayout::RowMajor, 512>; Pipe pipe(fifoMem, 0x0, LocalBase); // V2C:第二参数为 C2V 消费者缓冲基址(此处为 0),第三参数为 V2C 消费者缓冲基址 MatTile tile; TPOP<Pipe, MatTile, TileSplitAxis::TILE_NO_SPLIT>(pipe, tile); }

要点:V2C 场景下TPipe构造参数中第三位传入LocalBase(L1 消费者缓冲基址),第二位置 0。MatTile为列主序(BLayout::ColMajor)、行主序 S 布局(SLayout::RowMajor, 512)的矩阵 tile。若要 V2C 行为与 NPU 一致,生产者与消费者的布局必须匹配(如 NZ Vec → NZ Mat),布局转换需在TPUSH前完成,因为 A5 本地 FIFO 的TPOP只做地址绑定、不做布局转换。

GlobalData Slot Pop:GM 槽位视图直达

#include <pto/pto-inst.hpp> using namespace pto; template <typename T> AICORE void example_globaldata(__gm__ void *fifoMem) { constexpr uint32_t M = 128; constexpr uint32_t N = 128; constexpr uint32_t FlagID = 0; constexpr uint32_t FifoDepth = 2; using Pipe = TPipe<FlagID, Direction::DIR_C2V, M * N * sizeof(T), FifoDepth>; using SlotGlobal = GlobalTensor<T, Shape<1, 1, 1, M / 2, N>, Stride<1, 1, 1, N, 1>>; using VecTile = Tile<TileType::Vec, T, M / 2, N, BLayout::RowMajor, M / 2, N>; Pipe pipe(fifoMem, 0x0, 0x0); SlotGlobal slot; VecTile tile; TASSIGN(tile, 0x0); TPOP<Pipe, SlotGlobal, TileSplitAxis::TILE_UP_DOWN>(pipe, slot); TLOAD(tile, slot); TFREE<Pipe, SlotGlobal, TileSplitAxis::TILE_UP_DOWN>(pipe, slot); }

要点:SlotGlobal是五维GlobalTensorShape<1,1,1,M/2,N>Stride<1,1,1,N,1>),TPOP只把它绑定到槽位基地址(C2V 切分模式下施加向量子块偏移),随后用TLOAD(tile, slot)显式加载数据,最后必须用TFREE<Pipe, SlotGlobal, ...>归还槽位视图。该模式适合数据驻留 GM、避免本地缓冲拷贝的场景。

测试用例与验证路径

TPOP 的正确性在仓库中有成体系的 ST 测试覆盖,可用于对照学习和验证:

  • CPU 后端:tests/cpu/st/testcase/tpushpop/main.cpp 覆盖了 C2V/V2C 双方向、TILE_NO_SPLIT/TILE_UP_DOWN/TILE_LEFT_RIGHT乃至 ODD 切分、DIR_BOTH 并发、参数反转写法TPOP(dst, pipe)等多种场景;
  • A2A3 NPU 后端:tests/npu/a2a3/src/st/testcase/tpushpop_cv/tpushpop_cv_kernel.cpp、tests/npu/a2a3/src/st/testcase/tpushpop_cv_nosplit/tpushpop_cv_nosplit_kernel.cpp、tests/npu/a2a3/src/st/testcase/tpushpop_dir_both/tpushpop_dir_both_kernel.cpp 覆盖了 A2A3 平台上的 C2V/V2C 切分与非切分、DIR_BOTH 并发 pop;
  • 固定管道:tests/cpu/st/testcase/tpushpop_fixpipe/tpushpop_fixpipe_kernel.cpp 与 tests/npu/a2a3/src/st/testcase/tpushpop_fixpipe/tpushpop_fixpipe_kernel.cpp 验证固定 FlagID 管道下的 push/pop 配对。

阅读这些测试时,可以重点观察"每次 pop 前生产者如何 push、pop 后如何 free"的配对关系,以及切分模式下 AIV0/AIV1 两个子块如何各取一半。

关于 ASM 形式

当前公开的 PTO 汇编参考(见 docs/isa 目录)尚未为TPOP定义稳定的 PTO-AS 拼写。因此手写 CV FIFO 程序时应使用 C++ intrinsic 形式进行编程,汇编形式留待后续版本补充。

小结

TPOP 是 PTO CV 通信模型的核心消费者指令,它把"数据就绪等待、槽位地址计算、本地 tile 绑定/数据加载"封装为一次调用。编程时需要重点把握三件事:方向与缓冲区的匹配(C2V 用C2V_CONSUMER_BUF、V2C 用V2C_CONSUMER_BUF)、切分模式与 tile 形状的对应(消费者 tile 有效形状必须等于生产者推送窗口)、以及槽位归还时机(A5 上TFREE必须配对调用,GlobalData 流程在全部TLOAD完成后必须归还视图)。结合 docs/isa/TPUSH.md、docs/isa/TFREE.md 与 docs/isa/TALLOC.md 可以拼出完整的 CV FIFO 生命周期;更完整的指令背景可参考 PTO-Virtual-ISA-Manual.md。

【免费下载链接】pto-isaParallel Tile Operation (PTO) is a virtual instruction set architecture designed by Ascend CANN, focusing on tile-level operations. This repository offers high-performance, cross-platform tile operations across Ascend platforms.项目地址: https://gitcode.com/cann/pto-isa

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询