看了一下英文版文档,做一个个人的小总结
1.编码及解释
2.相关指令分类
指令根据mop分为unit-stride,index-unorder,strided,indexed-order四种;其中unit-stride根据lumop又可以分为四种,如图
3.常见名词
riscv-vector扩展指令有别于传统设计的一点是,采用了动态寄存器类型设定(dynamic register typing)。涉及的包括但不限于:
为了更好的理解load指令,需要知道下面几个概念:
SEW:元素位宽;LMUL:向量寄存器组数;vl:指令控制的元素数;VLEN:向量寄存器长度
不妨设VLEN=512b 使用vsetvli指令配置这些参数
t0=vl=LMUL*(VLEN/SEW)=8
4.unit-stride load
unit-stride load很简单,就是一个地址接着一个无间隔进行load,下面四个命令
第一条指令配置了元素宽度为8,但是寄存器配置为32,这是否会产生矛盾呢?答案是否定的。在指令执行过程中,操作的位宽为EEW,有别于寄存器配置的SEW。因此执行第一条指令会load EEW=8 vl=8 emul=1/8 ,加载在(rs1)地址开始的共8个8b数据到向量寄存器vd中(将内存上的地址分割成vl数量EEW宽度的数据),最终占有vd这个寄存器的len的1/8。
第二条指令也是同理,EEW=16 vl=8 emul=1/4 。
第三条指令EEW=SEW emul=lmul=1/2
第四条指令EEW=SEW*2 emul =1
可以发现,在vtype配置中,vl决定了指令控制的元素数,而不随指令的EEW的改变而改变。
5.stride load
也支持零步幅和负的步幅
和stride的区别在于,会从rs2这个寄存器中提取出一个常数作为stride,然后在(rs1)地址上的数据被分为位宽eew的数据时,间隔stride个数据进行读取,如图所示:
设置这种stride load的好处在于,可以很好地处理多维数组中的非连续数据
6.index load
index load和stride load的区别主要有两个。
其一是EEW负责vs2的数据格式,而SEW负责vd的数据格式
其二是index是访存地址的偏移而非元素的偏移
如图所示:
此类索引可以进行集中-分散操作,可以很好地支持稀疏矩阵的压缩。
7.whole register load(unit-stride、stride、index)
这条指令与上条指令就有所不同,与vl无关,而nf(n field)域起作用。nf可以理解为可以操作几个寄存器。指令的EEW决定了操作元素位宽,因此这条指令可以操作VLEN/EEW(每个filed操作几个EEW宽的数)*nf(filed数量)的数量的数。这条指令的作用是也是读取一个有效长度。为什么不直接改变vl呢。spec上给出了解释:
当不知道vector register的layout或者改变vl会产生很大的开销(增加复杂度)的时候会使用nf来代替vl进行操作。比较典型的有编译器发现寄存器溢出(compiler register spills,指的是寄存器容量不够)或者中断时的上下文切换等。
下面是segment unit-stride指令
分别是以eew=8,16,32,64,从(a0)开始一个地址一个地址读值,来填满v3
unit-stride:
stride:
index:
值得一提的是,向量寄存器和memory的数据类型是一样的,并且这里不论eew的值为多少,填充的总长度都是一定的(VLEN)。之所以可以设置eew,是为了可以提示后续指令处理的EEW
8.unit-stride fault-only-first
用于向量化的循环的退出。只有第一个元素会引起trap。
参考资料:riscv-v-spec-1.0 & Andes-RVV-Webinar