跳转至

程序序(Program Order, PO)

1. 什么是程序序

灵犀处理器按收到的指令与外部事件的先后顺序推进内部状态:你给它什么指令、以什么顺序给,它就按这个顺序“理解并生效”。 为了把这种“应该按什么顺序理解执行”的语义说清楚,我们定义了程序序(Program Order, PO)

  • PO = 从代码角度看,硬件对外表现应当等价的执行顺序
  • 硬件内部可能乱序或并行,但对外部可见的效果(寄存器、内存、Tile 的最终值)必须与 PO 一致。

2. 从“块头顺序”展开成“程序序”

灵犀的块指令由两部分组成:

  • 块头:配置“这块要干什么”(数据类型、维度、输入/输出 Tile 等)。
  • 块身:真正执行的步骤(搬运/乘累加/转换/回写等)。

编译时,块头会被排成一个线性顺序(可以理解为“调用点”的顺序)。之后,把每个块头展开成它的块身步骤,得到最终的程序序 PO。

两种展开方式

  1. 随机替换(不强制内部顺序) 块身里的几个动作可以并行或调度顺序不固定,只要整块在其他块之前/之后的大顺序不变即可。

  2. 顺序替换(内部顺序固定) 块身里的动作有明确先后,必须逐步执行——例如“先搬数据,再乘累加,最后回写”。

多数矩阵/向量计算块的块身都是顺序替换,因此最终 PO 通常是一个清晰的全序。


3. PTO ISA 0.58 的 CUBE 示例

PTO ISA 0.58 不存在隐藏的架构 ACC 状态,也没有 ACCCVT 块。CUBE 描述符始终指定显式 Local 目的 Tile D;ACC 形式还指定显式 Local 累加输入 C。

例 1:基础矩阵乘

BSTART.TMATMUL 及其维度、属性和 Tile 描述符展开为读取 A/B、执行矩阵乘、 写入 D;整个展开占据该块在程序序中的位置。

例 2:矩阵乘后的转换

格式或布局转换是一个单独的显式 Tile 操作,在 CUBE 块之后读取 D;不存在隐式 ACC→Tile 通路。程序序为:

TMATMUL 描述符 → 读取 A/B → 写 D → 转换描述符 → 读取 D → 写转换后的 Tile

例 3:带 scale 的累加

BSTART.TMATMULMX.ACC 读取 A、行 scale、B、列 scale 和累加输入 C,然后 写 D。当 D 与 C 相同时,操作读取 C 的旧值并写入新结果;否则 C 与 D 是独立的 显式 Tile。


4. PO 与“真实执行”的关系(再强调)

  • PO 是“应该表现成的顺序”:编译器、验证和上层框架都以此为准去理解程序语义。
  • 硬件内部可乱序/并行,但对外可见效果必须与 PO 等价
  • 后续诸如寄存器距离计算、内存一致性/顺序、屏障等约束,均以 PO 为基础。

5. 微观到宏观:如何落地到你的代码

  • 块头理解为“在时间线上占一个位置”的调用点
  • 顺序替换:把这个调用点用“具体步骤”替换掉;
  • 随机替换:你只强调“这是一组动作”,但这组内部顺序不重要(或由调度器决定);
  • 所有块按块头出现顺序排好,再逐个展开,你看到的就是整个程序的程序序

6. 总结

程序序(PO)= 代码层面,块头按出现顺序 → 用块身动作(顺序/随机)展开后的整体顺序。 硬件再怎么优化,外部观察到的行为都必须与这条“顺序线”一致。

这样写完,你在读任何一段灵犀代码时,都能清楚回答三件事:

  1. 先后关系:谁在前、谁在后;
  2. 展开内容:一个块头到底代表哪些具体动作;
  3. 可见语义:不管底层怎么并行,外部看到的结果与这条顺序一致。