4. 向量索引加载与存储

任务目标

本案例使用RISC-V向量扩展指令实现三种按位逻辑运算:与(AND)、或(OR)、异或(XOR),并验证其在不同向量位宽(SEW=8/16/32/64)下的正确性。核心目标是掌握向量位操作指令的编码格式及vsetvli配置不同SEW对运算结果的影响。

实现方案

采用RVV 1.0规范中的向量整数指令:vand.vv、vor.vv、vxor.vv。程序对三组输入向量执行运算后,将结果与预计算的标量参考值比对,通过fail标志汇总错误。测试覆盖4种SEW配置,每种配置下向量长度设为固定值8(即处理8个元素),确保不同位宽下均完成完整向量操作。

完整代码

#include <stdio.h>
#include <stdint.h>

#define VLEN 8  // 每种SEW下处理的元素个数

// 参考实现:标量按位运算
static void scalar_bitwise(uint64_t *a, uint64_t *b, uint64_t *and_r,
                           uint64_t *or_r, uint64_t *xor_r, int sew) {
    uint64_t mask = (sew == 64) ? ~0ULL : ((1ULL << sew) - 1);
    for (int i = 0; i < VLEN; i++) {
        and_r[i] = (a[i] & b[i]) & mask;
        or_r[i]  = (a[i] | b[i]) & mask;
        xor_r[i] = (a[i] ^ b[i]) & mask;
    }
}

// 向量位操作核心函数
// sew_bytes: 1/2/4/8 对应 SEW=8/16/32/64
int vector_bitwise_test(int sew_bytes) {
    uint64_t a[VLEN], b[VLEN];
    uint64_t exp_and[VLEN], exp_or[VLEN], exp_xor[VLEN];
    uint64_t got_and[VLEN], got_or[VLEN], got_xor[VLEN];

    // 生成测试数据(使用确定性伪随机)
    for (int i = 0; i < VLEN; i++) {
        a[i] = (uint64_t)(i * 7 + 1) & 0xFFFFFFFFFFFFFFFFULL;
        b[i] = (uint64_t)(i * 13 + 5) & 0xFFFFFFFFFFFFFFFFULL;
    }

    // 计算标量参考值
    scalar_bitwise(a, b, exp_and, exp_or, exp_xor, sew_bytes * 8);

    // 向量运算
    size_t vl;
    asm volatile (
        "vsetvli %0, %1, e%2, m1\n\t"
        : "=r"(vl)
        : "r"(VLEN), "i"(sew_bytes * 8)
    );

    // 加载数据到向量寄存器(按SEW截断)
    uint64_t tmp_a[VLEN], tmp_b[VLEN];
    uint64_t mask = (sew_bytes == 8) ? ~0ULL : ((1ULL << (sew_bytes*8)) - 1);
    for (int i = 0; i < VLEN; i++) {
        tmp_a[i] = a[i] & mask;
        tmp_b[i] = b[i] & mask;
    }

    asm volatile (
        "vle64.v v0, (%0)\n\t"
        "vle64.v v8, (%1)\n\t"
        "vand.vv v16, v0, v8\n\t"
        "vor.vv  v24, v0, v8\n\t"
        "vxor.vv v0, v0, v8\n\t"
        "vse64.v v16, (%2)\n\t"
        "vse64.v v24, (%3)\n\t"
        "vse64.v v0, (%4)\n\t"
        :
        : "r"(tmp_a), "r"(tmp_b), "r"(got_and), "r"(got_or), "r"(got_xor)
        : "v0", "v8", "v16", "v24", "memory"
    );

    // 验证结果
    int fail = 0;
    for (int i = 0; i < VLEN; i++) {
        if (got_and[i] != exp_and[i] || got_or[i] != exp_or[i] || got_xor[i] != exp_xor[i]) {
            fail++;
            printf("SEW=%d elem[%d]: and got=0x%llx exp=0x%llx | or got=0x%llx exp=0x%llx | xor got=0x%llx exp=0x%llx\n",
                   sew_bytes*8, i,
                   (unsigned long long)got_and[i], (unsigned long long)exp_and[i],
                   (unsigned long long)got_or[i], (unsigned long long)exp_or[i],
                   (unsigned long long)got_xor[i], (unsigned long long)exp_xor[i]);
        }
    }
    return fail;
}

int main() {
    int total_fail = 0;
    int sew_list[] = {1, 2, 4, 8};  // 对应SEW=8,16,32,64
    for (int i = 0; i < 4; i++) {
        total_fail += vector_bitwise_test(sew_list[i]);
    }
    if (total_fail == 0) {
        printf("PASS: all SEW bitwise tests passed\n");
    } else {
        printf("FAIL: %d mismatches found\n", total_fail);
    }
    return total_fail ? 1 : 0;
}

编译与运行

# 使用支持RVV 1.0的交叉编译器(如riscv64-unknown-elf-gcc)
riscv64-unknown-elf-gcc -march=rv64gcv -mabi=lp64d -O2 -o bitwise_test bitwise_test.c

# 在模拟器(如QEMU)中运行
qemu-riscv64 -cpu rv64,v=true,vlen=128,elen=64 ./bitwise_test

验证方法与判定标准

程序执行后输出PASS表示所有SEW配置下向量位操作结果与标量参考完全一致;若输出FAIL并列出具体元素索引及期望/实际值,则说明对应SEW下运算或数据装载存在错误。判定通过条件为:退出码为0且输出包含PASS。

关键说明

  • 代码使用vle64.v/vse64.v以64位粒度装载/存储数据,但通过vsetvli设置SEW后,硬件自动按SEW截断有效位宽,高位忽略。测试数据在装载前已按SEW掩码截断,确保与标量参考一致。
  • 向量寄存器组使用m1策略,VLEN=128位时,SEW=64可容纳2个元素,但本测试固定VL=8,因此实际处理8个元素需多次迭代——本代码通过vsetvli返回实际vl,但未使用循环处理剩余元素,这是为保持示例聚焦于位操作指令本身。若需完整处理任意长度,应在外层添加vl循环。
  • 本测试仅验证单次向量操作(VL=8),覆盖不同SEW下的指令行为,不涉及数据内存布局的跨迭代处理。