本文档链接 https://blog.youkuaiyun.com/weixin_42487906/article/details/115437890
RISCV向量指令集学习
参考链接
https://github.com/riscv/riscv-v-spec
RSIC-V——指令集spec阅读笔记——向量扩展0.9
RISC-V Vector Extension学习笔记
向量指令集用例
可以对照下面的说明来看这个例子
.text
.balign 4
.global vvaddint32
# vector-vector add routine of 32-bit integers
# void vvaddint32(size_t n, const int*x, const int*y, int*z)
# {
for (size_t i=0; i<n; i++) {
z[i]=x[i]+y[i]; } }
#
# a0 = n, a1 = x, a2 = y, a3 = z
# Non-vector instructions are indented
vvaddint32:
vsetvli t0, a0, e32, ta,ma # Set vector length based on 32-bit vectors
vle32.v v0, (a1) # Get first vector
sub a0, a0, t0 # Decrement number done
slli t0, t0, 2 # Multiply number done by 4 bytes
add a1, a1, t0 # Bump pointer
vle32.v v1, (a2) # Get second vector
add a2, a2, t0 # Bump pointer
vadd.vv v2, v0, v1 # Sum vectors
vse32.v v2, (a3) # Store result
add a3, a3, t0 # Bump pointer
bnez a0, vvaddint32 # Loop back
ret # Finished
名词解释
ELEN | 向量中单个元素的最大长度,bit为单位,要求ELEN>=8,且为2的幂 |
VLEN | 向量寄存器的位数,bit为单位,要求VLEN ≥ ELEN,并且必须是2的幂 |
SLEN | 分段距离(The striping distance in bits),要求必须为VLEN ≥ SLEN ≥ 32,并且必须为2的幂,这个暂时没懂 |
SEW | 标准元素宽度,以bit为单位,指向量中一个元素占向量寄存器中的位数 |
LMUL | 向量寄存器分组数 |
寄存器定义 | |
v0-v31 | 向量数据寄存器,共32个,固定位宽为VLEN位 |
vtype | 向量数据类型寄存器,并且只能通过vsetvl{i}指令进行更新,向量类型还决定了每个向量寄存器中元素的排布,以及如何对多个向量寄存器进行分组。 |
vsew[2:0] | 变化的标准元素宽度(SEW, standard element width)值通过vsew中的值设置。默认情况下,向量寄存器被视为分成 VLEN / SEW 个标准宽度元素。 |
vlmul[2:0 ] | 向量寄存器分组,分成一组的多个向量寄存器可以当作一个操作数来用,用来存放一个向量,该值也可以是小数,用来等效增加向量数据寄存器的数量 |
VLMAX | VLMAX = LMUL * VLEN / SEW, |
vta和vma | 这两位元素值在执行向量指令期间分别修改了目标尾部元素(Tail Elements)和非活跃的掩码元素(Inactive masked-off Elements)的行为。 |
vill | vill位用于编码先前的vsetvl{i} 指令试图向vtype写入不支持的值 |
vl | 向量长度寄存器,以存放向量总共有多少个元素,只能通过vsetvli和vsetvl指令进行更新。 |
vlen | 向量寄存器长度寄存器,是一个常数,保存值 VLEN / 8,即向量寄存器长度(以字节为单位)。 |
vstart | vstart是一个可读可写的CSR寄存器,指定向量指令要执行的第一个元素的索引。每个向量指令执行完后,该寄存器清零。 |
vcsr | 向量控制和状态寄存器vcsr,保存定点舍入的方式和饱和状态 |
向量元素到向量寄存器的映射
根据当前的 SEW 和 LMUL 设置以及 ELEN,VLEN 和 SLEN 的值,怎么将不同宽度的元素存放到矢量寄存器的字节中。并且使用最少位数的最低有效字节将元素放到每个向量寄存器中。
当 VLEN=SLEN , LMUL=1 时,从向量寄存器的最低有效位到最高有效位依次对元素进行简单排布。当 LMUL <1 时,仅使用向量寄存器中的第一个 LMUL * VLEN / SEW 元素。 向量寄存器中的剩余空间被视为尾部的一部分。
VLEN = SLEN 且 LMUL > 1时, 将向量寄存器分组后,根据向量寄存器组中的向量寄存器来划分组中的元素。当 SLEN = VLEN 时,按元素顺序将其放在组中的每个向量寄存器中,填满一个向量寄存器后,便移至组中下一个编号最高的向量寄存器。
向量 ISA 的设计目的是支持混合宽度操作,并且不需要大量显式的额外的重排指令,也不需要大量额外的数据路径(datapath)布线。在对不同精度值的向量进行操作时,推荐的做法是动态修改 vtype 值,从而使得 SEW/LMUL 为常量(因此 VLMAX 常量)。向量寄存器分组因子(LMUL)按相关元素大小增加,这样每个向量寄存器组可以保存相同数量的向量元素(本例中 VLMAX=16 ),从而简化 stripmining 代码。下表列出了执行混合宽度操作的循环的每个可能的 SEW / LMUL 操作点。 每列代表一个恒定的 SEW / LMUL 操作点。 表中列出的内容是 LMUL 值,该值生成该列的 SEW / LMUL 值。 在同一列中, LMUL 的值不同,但 VLMAX 值相同。(可以保证每个向量寄存器组中的向量元素数量相同)
配置寄存器操作指令
vsetvli/vsetvl
用例
vsetvli rd, rs1, vtypei # rd = new vl, rs1 = AVL, vtypei = new vtype setting
vsetvl rd, rs1, rs2 # rd = new vl, rs1 = AVL, rs2 = new vtype value
解释
指令根据其参数设置 vtype 和 vl CSRs,并将 vl 的新值写入 rd。
两个指令的区别:vtypei是个立即数,rs2是个寄存器
AVL=应用程序向量长度
vsetvl{i}指令首先根据 vtype 参数,设定 VLMAX ,然后设置vl服从以下约束:
- 如果 AVL ≤ VLMAX ,则 vl = AVL
- 如果 AVL < (2 * VLMAX),则ceil(AVL / 2) ≤ vl ≤ VLMAX
- 如果 AVL ≥ (2 * VLMAX),则 vl = VLMAX
- 如果输入相同的 AVL 和 VLMAX 值,则任何实现中,v1 都是确定的
满足之前提及的规则:
如果 AVL = 0,则 vl = 0
如果 AVL > 0,vl > 0
vl ≤ VLMAX
vl ≤ AVL
从 vl 中读取的值(用作 vsetvl{i} 的 AVL 参数时)会在 vl 中产生相同的值,前提是所得的 VLMAX 等于读取vl时的 VLMAX 值
向量操作指令中的掩码
掩码vm
在指令中占一位,使用v0寄存器存放掩码值,vm为1时不启动掩码,vm为0时,当v0的相应位为1,目标向量寄存器写入相应的值
掩码向量的加载操作不会更新目标向量寄存器组中的非活跃元素。掩码向量的存储操作只更新内存中的活跃元素。
向量加载和存储指令(访存)
mop 访存时选择访存模式
支持连续访存,跨步访存和按索引访存
lumop/sumop
还没搞懂是做什么用的 fault only first似乎可以简化循环操作
向量加载/存储的宽度编码
- Mem bits(内存位)是内存中访问的每个元素的大小。
- Reg bits(寄存器位)是寄存器中访问的每个元素的大小。
向量单步幅跨步(Unit-Stride)指令
#vd destination, rs1 base address, vm is mask encoding (v0.t or <missing>)
vle32.v vd, (rs1), vm # 32-bit loads
l代表load,e32代表向量元素长32
#vs3 store data, rs1 base address, vm is mask encoding (v0.t or <missing>)
vse64.v vs3, (rs1), vm # 64-bit stores
s代表save
向量跨步(Strided)指令
# vd destination, rs1 base address, rs2 byte stride
vlse8.v vd, (rs1), rs2, vm # Load bytes separated by stride
vls中l代表load,s代表stride,rs2存放跨步值
# vs3 store data, rs1 base address, rs2 byte stride
vsse128.v vs3, (rs1), rs2, vm # Store 128b values separated by stride.
vsse128中,第一个s是save,第二个是stride
向量索引(indexed)指令
# vd destination, rs1 base address, vs2 indices
vlxei16.v vd, (rs1), vs2, vm # vs2 data EEW = SEW, indices EEW = 16b
有序加载,vs2存放索引,vs2中元素大小为16bit
# Vector unordered-indexed store instructions
vsuxei64.v vs3, (rs1), vs2, vm # SEW data, 64b indices
无序存储,vs2中元素大小为64bit
向量原子操作
原子操作指令 暂时用不到
向量计算指令
向量的算术指令使用与 OP-FP 相邻的新的主要操作码1010111。 funct3 字段的三位用于定义向量指令的子类。
向量与标量之间的运算可以采取三种可能的形式,但无论采取哪种形式,都要从 vs2 指定的向量寄存器组中获取一个操作数向量,并从三个替代源之一获取第二个标量源操作数。
- 对于整数运算,标量可以是编码在 rs1 字段中5位立即数。该值通过符号扩展或加零扩展到SEW 位。
- 对于整数运算,可以从rs1 指定的标量寄存器x中提取标量。如果 XLEN > SEW,则使用x的最低有效SEW位。如果 XLEN < SE