RVV v1.0
2021 年 12 月批准,工业界已有多家采用者
CS152 · 第 19 课
这一课把向量处理器的抽象概念——向量长度、通道、掩码——变成一份真实、已经批准的 ISA 规范:RISC-V "V" 向量扩展(2021 年 12 月批准)。这也正是 Lab 4 要动手编写的汇编代码所依据的规范。
RISC-V "V" 向量扩展规范 v1.0 于 2021 年 12 月正式批准;已知的采用者包括 Alibaba、Andes、Semidynamics、SiFive;也是欧洲处理器计划(European Processor Initiative, EPI)超算项目的基础。相对标量状态(x0-x31、f0-f31、fcsr),向量扩展新增的处理器状态包括:32 个向量寄存器 v0-v31(每个 VLEN 位宽,具体数值由实现决定)、向量长度寄存器 vl、向量类型寄存器 vtype、vstart(用于陷阱处理时恢复部分执行的向量指令)、vrm/vxsat(定点舍入模式/饱和标志,也出现在 vcsr 里)、以及只读的 vlenb(以字节为单位给出向量寄存器长度)。
vtype 寄存器编码了几个关键字段:vsew[2:0] 决定SEW(Selected Element Width,选定的元素宽度):
\[ \text{SEW} = 8 \times 2^{\text{vsew}} \]
把 vsew 取值 0 到 7 依次代入,得到 SEW = 8, 16, 32, 64, 128, 256, 512, 1024 位——这是一列每次翻倍的等比数列,重新验算与幻灯片给出的 SEW 数值完全一致。(幻灯片表格里列出的具体 3 位二进制编码本身,第 3、5、6 行的印刷顺序看起来不完全是一个干净的 0-7 递增二进制序列——这更像是原始幻灯片的排版/转录问题,而不是 SEW 数值本身的错误:SEW 数值序列每次翻倍是完全正确、内部自洽的。)
vlmul[2:0] 决定LMUL(向量寄存器长度乘数):
\[ \text{LMUL} = 2^{\text{vlmul}}, \quad \text{取值范围 } \tfrac{1}{8} \text{ 到 } 8 \]
LMUL 大于 1 时,把多个物理向量寄存器合并成"更少但更长"的逻辑向量寄存器——LMUL=2 时只能用偶数编号寄存器(v0, v2, …),LMUL=4 时每 4 个一组(v0, v4, …),以此类推。
VLMAX 公式:一条向量指令到底能处理多少个元素?
\[ \text{VLMAX} = \frac{\text{LMUL} \times \text{VLEN}}{\text{SEW}} \]
配置指令 vsetvli/vsetivli/vsetvl 会根据请求的 SEW/LMUL 计算出 VLMAX,再取 vl = min(AVL, VLMAX)(AVL 是程序请求的应用向量长度)。vsetvli(寄存器-立即数形式,最常用)、vsetivli(立即数-立即数形式,向量长度静态已知时使用)、vsetvl(寄存器-寄存器形式,"仅用于上下文保存/恢复")三者的区别仅在于操作数的来源。
混合宽度实例(重新验算):VLEN=128 位,保持 SEW/LMUL=8 不变:
四次重新计算都得到 16,与幻灯片完全一致——这正是这个例子想说明的核心洞察:只要保持 SEW/LMUL 的比值不变,无论元素宽度怎么变,一条向量指令能处理的元素个数(VLMAX)就保持不变。这让同一段向量代码可以在不同数据宽度之间复用,只需调整 SEW/LMUL 配置。
RVV 提供三类 load/store:单位步长(unit-stride)——vle8/16/32/64.v、vse8/16/32/64.v,按连续地址访存;跨步(strided)——vlse*/vsse*,按固定步长跳跃访存;索引(indexed,即 gather/scatter)——vluxei*/vloxei*(无序/有序 load)、vsuxei*/vsoxei*(无序/有序 store)。索引访存的地址数据宽度在指令中单独编码,而实际数据大小由 vtype.vsew 字段决定;有序(ordered)版本对内存一致性/精确故障的处理更严格,无序(unordered)版本给硬件更多重排空间。
指令编码中的一个 vm 位决定该指令是否受 v0 掩码寄存器控制(由于 32 位编码空间有限,掩码寄存器被固定为 v0;未来的 64 位编码扩展将允许谓词存放在任意寄存器)。RVV 规范精确定义了向量寄存器中每个元素属于哪一类:预启动元素(索引小于 vstart,不会引发异常、不会更新目的寄存器);主体元素(索引在 [vstart, vl) 之间),又分为激活元素(掩码为真,可以引发异常、可以更新目的寄存器)和未激活元素(掩码为假,默认不更新,除非设置了 vma=1 的"掩码不可知"策略,此时可能被覆写为全 1);尾部元素(索引超出 vl,默认不更新,除非设置了 vta=1 的"尾部不可知"策略)。"不可知(agnostic)"策略允许硬件实现选择"保持不变"或"覆写为全 1"中的任意一种,为硬件实现留出优化空间。
vsetvli 根据请求的 AVL 和当前配置计算出的 VLMAX 共同决定,是一个可以按需调整的运行时值。问题 1:VLEN=128 位,SEW=16 位,LMUL=2,此时 VLMAX(一条向量指令最多处理的元素个数)是多少?
问题 2:为什么在 VLEN=128 位固定的情况下,SEW=8/LMUL=1、SEW=16/LMUL=2、SEW=32/LMUL=4、SEW=64/LMUL=8 这四种配置都能得到相同的 VLMAX=16?
问题 3:RVV 规范中的"预启动元素(prestart elements)"是什么?
2021 年 12 月批准,工业界已有多家采用者
SEW=8×2^vsew,LMUL=2^vlmul,共同决定元素宽度与寄存器分组
LMUL×VLEN/SEW,保持比值不变即可跨宽度复用代码
预启动/主体(激活/未激活)/尾部,配合不可知/不受扰策略
sp23/lecture_slides/L19_RISCV_Vectors.pdf(UC Berkeley CS152/CS252A, Spring 2023;原作者 Krste Asanović,本讲由 Albert Ou 修订)