块指令执行模型¶
基于灵犀指令集架构实现的处理器以块指令为单位执行程序。顺序执行的处理器需要按照程序语义的顺序一条条执行块指令。而乱序执行的处理器,可乱序执行块指令但要顺序提交。
块指令的执行过程包括三个步骤:
- 准备(Prepare):一层调度器取指和分配一层寄存器到二层执行单元,作为它的数据输入输出。
- 执行(Execute):二层执行单元执行本块的块体指令。
- 提交(Commit):二层执行单元提交结果到一层调度器,以支持后序块的执行。
块指令执行模型示意图如下:

块指令初始化¶
块指令执行前,需要初始化块指令的内部状态,其中包括:
保存块参数¶
执行块指令前,处理器需要保存当前块的BPC和跳转参数等信息到私有寄存器BARG中,以便块执行结束后能够正确地跳转到下一个块指令。其中包括:
- 设置本块的块类型到BARG寄存器的 BlockType 字段,用于分配到对应的块引擎执行以及异常处理。
- 对于支持乱序执行的处理器,应将本块指令的BPC记录到BARG寄存器的BPC 字段。
- 根据块头中的跳转参数保存下个块的地址到BARG寄存器的 BPCN 字段。对于实现了分支预测的处理器,可以根据分支预测单元记录的信息设置该字段。
- 根据块头中的跳转参数设置BARG寄存器的 TYPE 字段。
- 记录本块的屏障属性到BARG寄存器的 AQ,RL 字段。
- 如果是分离块,将块内的本地返回地址记录到BARG寄存器的 LRA 字段。
BlockType -> BARG.BlockType; // 记录本块的块类型
CurrentBPC -> BARG.BPC; // 保存本块BPC
NextBPC -> BARG.BPCN; // 记录下一个块的BPC
BranchType -> BARG.TYPE; // 记录本块跳转方式
aq,rl -> BARG.AQ, RL; // 记录本块的屏障属性
TPC(B.TEXT)+4 -> BARG.LRA; // 记录本地返回地址(分离块)
不同跳转类型下,NextBPC的计算方式有所不同:
- FALL:NextBPC是顺延块的BPC。
- DIRECT,CALL和COND:
NextBPC = BPC + BNextOffset。其中BNextOffset表示块头中指示的跳转偏移。 - IND,ICALL和RET:NextBPC通过分支预测单元的预测得到。
全局寄存器状态备份¶
块处理器在块初始化阶段根据块体或块头中给出的寄存器声明,识别输入/输出寄存器集合,并对相应的通用全局寄存器(GGPR)执行重命名或备份。重命名用于解依赖与提交期正确回写;备份用于在块内通过私有形参寄存器进行稳定访问。
一体块(块头和块体连续存储):
- 解析块体指令的寄存器操作数,识别输入/输出GGPR,并在块初始化时对这些寄存器执行重命名。
- 示例:以下为一条三输入、二输出的块指令。在初始化阶段,块处理器对
a0、a3、sp、ra执行重命名。
分离块(块头和块体分开存储):
- 按块头指令 B.IOR 指示的寄存器信息,对GGPR进行备份与记录:
- 将输入GGPR的当前内容备份到块内输入形参寄存器 RI0~RI11。
- 将输出GGPR的当前内容备份到块内输出形参寄存器 RO0~RO3。
- 将输出寄存器编号写入 BARG 的 RegDst0~RegDst3,供块提交时使用。
- 若为数据(Tile)块,还需依据块头指令 B.IOT 建立 Tile 形参与实际 Tile 寄存器的映射:
- 将输入 Tile 形参(如 TA、TB 等)映射到 B.IOT 指定的输入 Tile 寄存器;
- 将输出 Tile 形参(如 TO 等)映射到 B.IOT 指定的输出 Tile 寄存器;
伪代码示例:
// 拷贝输入GGPR内容,RegSrc[0~11]为B.IOR中输入GGPR的编号
RI0 = RegSrc0;
RI1 = RegSrc1;
...
RI11 = RegSrc11;
// 拷贝输出GGPR内容,RegDst[0~3]为B.IOR中输出GGPR的编号
RO0 = RegDst0;
RO1 = RegDst1;
...
RO3 = RegDst3;
// 记录输出GGPR编号至BARG
RegDst0 -> BARG.RegDst0;
RegDst1 -> BARG.RegDst1;
...
RegDst3 -> BARG.RegDst3;
// 建立输入输出Tile映射关系
SrcTile0 -> TA;
SrcTile1 -> TB;
...
SrcTile7 -> TH;
DstTile0 -> TO;
DstTile1 -> TO1;
DstTile2 -> TO2;
DstTile3 -> TO3;
若块内形参寄存器没有被初始化,那么块体内访问时其结果是未定义的,并且架构不保证执行结果的正确性。
模版块(仅块头描述,无块体):
- 依据块头指令 B.IOR/B.IOT 表达的寄存器信息执行重命名与解依赖,确保模板实例化或后续拼接时的寄存器一致性。
- 由于模板块没有块体指令,不需要将寄存器内容备份到块内私有寄存器(不使用 RI/RO 等私有备份)。
块指令执行¶
块指令作为灵犀指令集的基本组织单元,其执行流程依据块类型和块体执行模式有所差异。并且,一体块和分离块形式下定义的块指令在控制流、寄存器访问和指令调度等方面具有不同的执行机制,详见块指令执行机制。
块体的具体执行方式分为标量模式、串行模式和并行模式,详见块体执行方式。在支持乱序执行的块引擎中,块体内的指令可乱序调度执行,但需保证指令按顺序提交,确保执行结果的正确性。
指令输入输出¶
- 一体块内的块体指令可以直接使用全局寄存器(GGPR)作为输入输出,并且写是立即生效的,读的是前序块体指令更新后的值。
- 一体块内的块体指令允许重复对同一个全局寄存器进行写操作。
- 分离块内的块体指令不支持直接读写全局寄存器,只能读写块内形参寄存器RI和RO。
- 分离块内的块体指令不允许重复写同一个全局寄存器,否则触发 重复设置全局寄存器异常。
例如如下的一体块指令:
在执行第一条指令I1时,对 sp寄存器的更新是立即生效的,不需要等待整个块提交。后续I3指令使用的sp值即是I1指令输出的sp值。
访问系统状态¶
块体内可以通过以下指令访问系统状态:
访存和访问系统寄存器都是立即生效的,不会等待块指令提交。
灵犀指令集定义的指令块是一种弱化的指令块。块指令只定义了寄存器的申请和释放,不对访存有强的原子性约束。
除非对块指令额外定义了原子属性,否则块内访存系统看到的是独立的Load和Store请求,访存请求已经没有了块的概念。对于带有原子属性的块指令,其块内有对访存额外的限制,详细请看B.CATR中关于原子属性的定义。
跳转参数设置¶
对于间接跳转类型的 IND,ICALL 和RET 块,需要通过块体指令来计算其跳转目标块的地址,并设置到BARG寄存器的BPCN字段。
对于条件跳转类型的 COND 块,块内动态判断是否满足跳转条件,并设置到BARG寄存器的TAKEN字段。
对于调用类型的 CALL 和 ICALL 块,块内需要将其顺延块的BPC保存到Ra寄存器中,以便在调用返回时能够正确地跳转到下一个块指令。
异常和中断¶
在块指令执行的过程中,允许触发中断和异常。处理方法请见中断和异常介绍。
块指令提交¶
块指令提交时会根据BARG寄存器的内容,对本块中的指令进行提交。其中包括:
- 对于分离块,块指令按照BARG寄存器中记录的 RegDst0~RegDst3 信息,将块内形参寄存器 RO0~RO3 的内容更新到对应的全局寄存器中。
- 块指令按照BARG寄存器的 BPCN 和 Taken 字段更新BPC,并跳转。
- 块指令提交后,BARG寄存器的内容会被清空,以便下一次块指令执行时重新初始化。
需要特殊注意的是,块指令提交时 不允许释放块内私有寄存器资源,并且继承给执行在相同块引擎的下个块指令使用。
block_a:
BSTART.STD FALL
inst0 xx, xx, ->t
inst1 xx, ->u
inst2 xx, xx, ->Rx
inst3 xx, xx, ->t
# block_a执行结束后保留T和U寄存器,继承给后序块指令
block_b:
BSTART.STD DIRECT
inst4 t#2, 10, ->t ; 输入t#2索引指令inst0的结果
inst5 u#1, xx, ->Rx ; 输入u#1索引指令inst1的结果
...
基于这种继承机制,当一个块指令触发异常并跳转到异常处理程序时,软件可以通过一个新块来保存异常块的状态,以便进行灵活的状态迁移和调度。