Saturate vl in RvvFrontEnd.sv Change-Id: Ia5215d18eec10b52aaf83c6a48985971e0e5602f
diff --git a/hdl/verilog/rvv/design/RvvFrontEnd.sv b/hdl/verilog/rvv/design/RvvFrontEnd.sv index 97d7e67..c0c2818 100644 --- a/hdl/verilog/rvv/design/RvvFrontEnd.sv +++ b/hdl/verilog/rvv/design/RvvFrontEnd.sv
@@ -130,6 +130,8 @@ // Update configuration architectural state RVVConfigState inst_config_state [N:0]; + logic [31:0] avl [N-1:0]; + logic [31:0] vlmax [N-1:0]; logic is_setvl [N-1:0]; always_comb begin inst_config_state[0] = config_state_q; @@ -138,20 +140,31 @@ inst_config_state[0].xsat = vxsat_i; for (int i = 0; i < N; i++) begin inst_config_state[i+1] = inst_config_state[i]; + avl[i] = 0; + vlmax[i] = 0; is_setvl[i] = 0; if (valid_inst_q[i] && (inst_q[i].opcode == RVV) && (inst_q[i].bits[7:5] == 3'b111)) begin if (inst_q[i].bits[24] == 0) begin // vsetvli - inst_config_state[i+1].vl = reg_read_data_i[2*i]; + // Set AVL based on encoding (see Section 6.2 of RVV spec) + unique case (inst_q[i].bits[12:8]) + 0: unique case (inst_q[i].bits[4:0]) + 0: avl[i] = inst_config_state[i].vl; // rd = x0, rs1 = x0 + default: avl[i] = 32'hFFFFFFFF; // rd != x0, rs1 = x0 + endcase + default: avl[i] = reg_read_data_i[2*i]; // rs1 != x0 + endcase + + avl[i] = reg_read_data_i[2*i]; inst_config_state[i+1].lmul = RVVLMUL'(inst_q[i].bits[15:13]); inst_config_state[i+1].sew = RVVSEW'(inst_q[i].bits[18:16]); inst_config_state[i+1].ta = inst_q[i].bits[19]; inst_config_state[i+1].ma = inst_q[i].bits[20]; is_setvl[i] = 1; end else if (inst_q[i].bits[24:23] == 2'b11) begin // vsetivli - inst_config_state[i+1].vl = + avl[i] = {{(`VL_WIDTH - 5){1'b0}}, inst_q[i].bits[12:8]}; inst_config_state[i+1].lmul = RVVLMUL'(inst_q[i].bits[15:13]); inst_config_state[i+1].sew = RVVSEW'(inst_q[i].bits[18:16]); @@ -159,7 +172,14 @@ inst_config_state[i+1].ma = inst_q[i].bits[20]; is_setvl[i] = 1; end else if (inst_q[i].bits[24:23] == 2'b10) begin // vsetvl - inst_config_state[i+1].vl = reg_read_data_i[2*i]; + // Set AVL based on encoding (see Section 6.2 of RVV spec) + unique case (inst_q[i].bits[12:8]) + 0: unique case (inst_q[i].bits[4:0]) + 0: avl[i] = inst_config_state[i].vl; // rd = x0, rs1 = x0 + default: avl[i] = 32'hFFFFFFFF; // rd != x0, rs1 = x0 + endcase + default: avl[i] = reg_read_data_i[2*i]; // rs1 != x0 + endcase inst_config_state[i+1].lmul = RVVLMUL'(reg_read_data_i[(2*i) + 1][2:0]); inst_config_state[i+1].sew = @@ -170,8 +190,8 @@ end end - // Compute legality of vtype. if (is_setvl[i]) begin + // Compute legality of vtype. unique case (inst_config_state[i+1].sew) SEW8: unique case(inst_config_state[i+1].lmul) @@ -196,15 +216,38 @@ endcase default: inst_config_state[i+1].vill = 1; endcase - end + // Compute vl to set (saturating with necessary) + unique case (inst_config_state[i+1].lmul) + LMUL1_8: vlmax[i] = ((`VLENB)/8) >> inst_config_state[i+1].sew; + LMUL1_4: vlmax[i] = ((`VLENB)/4) >> inst_config_state[i+1].sew; + LMUL1_2: vlmax[i] = ((`VLENB)/2) >> inst_config_state[i+1].sew; + LMUL1: vlmax[i] = (`VLENB) >> inst_config_state[i+1].sew; + LMUL2: vlmax[i] = (2*(`VLENB)) >> inst_config_state[i+1].sew; + LMUL4: vlmax[i] = (4*(`VLENB)) >> inst_config_state[i+1].sew; + LMUL8: vlmax[i] = (8*(`VLENB)) >> inst_config_state[i+1].sew; + default: vlmax[i] = 0; + endcase + + if (inst_config_state[i+1].vill) begin + // If illegal, set to 0. See end of section 6.1 of RVV spec. + inst_config_state[i+1].vl = 0; + end else if (avl[i] > vlmax[i]) begin + // One possible valid impl according to 6.3 of RVV spec. + inst_config_state[i+1].vl = vlmax[i]; + end else begin + inst_config_state[i+1].vl = avl[i]; + end + end end end always_ff @(posedge clk or negedge rstn) begin if (!rstn) begin - config_state_q.vill <= 1; // Config is illegal on reset. - config_state_q.vl <= 16; + // Per Section 3.11 of RVV spec, the recommended state on reset is + // vill is set, with the remain vtype bits and vl being set to 0. + config_state_q.vill <= 1; + config_state_q.vl <= 0; config_state_q.vstart <= 0; config_state_q.ma <= 0; config_state_q.ta <= 0; @@ -226,10 +269,10 @@ always_comb begin for (int i = 0; i < N; i++) begin unaligned_trap_valid[i] = valid_inst_q[i] && !is_setvl[i] && - inst_config_state[i].vill; + inst_config_state[i+1].vill; unaligned_trap_data[i] = inst_q[i]; unaligned_cmd_valid[i] = valid_inst_q[i] && !is_setvl[i] && - !inst_config_state[i].vill; + !inst_config_state[i+1].vill; // Combine instruction + arch state into command `ifdef TB_SUPPORT @@ -237,7 +280,7 @@ `endif unaligned_cmd_data[i].opcode = inst_q[i].opcode; unaligned_cmd_data[i].bits = inst_q[i].bits; - unaligned_cmd_data[i].arch_state = inst_config_state[i]; + unaligned_cmd_data[i].arch_state = inst_config_state[i+1]; // TODO: Handle rs propagation for loads/stores unaligned_cmd_data[i].rs1 = inst_q[i].bits[7] ? reg_read_data_i[2*i] : 0; @@ -246,7 +289,7 @@ reg_write_valid_o[i] = is_setvl[i]; reg_write_addr_o[i] = inst_q[i].bits[4:0]; reg_write_data_o[i] = - {{(`XLEN-`VL_WIDTH){1'b0}}, inst_config_state[i].vl}; + {{(`XLEN-`VL_WIDTH){1'b0}}, inst_config_state[i+1].vl}; end end
diff --git a/tests/cocotb/BUILD b/tests/cocotb/BUILD index 07c64ac..e2cabff 100644 --- a/tests/cocotb/BUILD +++ b/tests/cocotb/BUILD
@@ -167,6 +167,7 @@ "core_mini_vmsof_test", "core_mini_vmsif_test", "core_mini_vill_test", + "core_mini_vl_test", ] # END_TESTCASES_FOR_rvv_assembly_cocotb_test
diff --git a/tests/cocotb/rvv/vcsr_test.cc b/tests/cocotb/rvv/vcsr_test.cc index a2612c5..9541df9 100644 --- a/tests/cocotb/rvv/vcsr_test.cc +++ b/tests/cocotb/rvv/vcsr_test.cc
@@ -21,12 +21,16 @@ uint32_t lmul __attribute__((section(".data"))) = 0; uint32_t vl __attribute__((section(".data"))) = 16; uint32_t vtype __attribute__((section(".data"))) = ~0; +uint32_t result_vl __attribute__((section(".data"))) = ~0; int main(int argc, char **argv) { uint32_t vtype_to_write = (vma << 7) | (vta << 6) | (sew << 3) | lmul; - asm volatile("vsetvl x0, %0, %1": : "r"(vl), "r"(vtype_to_write)); - uint32_t vtype_to_read; - asm volatile("csrr %0, vtype" : "=r"(vtype_to_read)); - vtype = vtype_to_read; + asm volatile( + "vsetvl %[result_vl], %[vl], %[vtype_to_write];" + "csrr %[vtype], vtype;" + : [result_vl] "=r" (result_vl), + [vtype] "=r" (vtype) + : [vl] "r"(vl), + [vtype_to_write] "r"(vtype_to_write)); return 0; }
diff --git a/tests/cocotb/rvv_assembly_cocotb_test.py b/tests/cocotb/rvv_assembly_cocotb_test.py index c2e1aa8..9de0f2c 100644 --- a/tests/cocotb/rvv_assembly_cocotb_test.py +++ b/tests/cocotb/rvv_assembly_cocotb_test.py
@@ -426,3 +426,68 @@ mcause_result = ( await core_mini_axi.read_word(mcause_addr)).view(np.uint32)[0] assert (mcause_result == 0x2) + +@cocotb.test() +async def core_mini_vl_test(dut): + """Testbench to test vsetvl instruciton saturate vl correctly.""" + # Test bench setup + core_mini_axi = CoreMiniAxiInterface(dut) + await core_mini_axi.init() + await core_mini_axi.reset() + cocotb.start_soon(core_mini_axi.clock.start()) + r = runfiles.Create() + + elf_path = r.Rlocation("kelvin_hw/tests/cocotb/rvv/vcsr_test.elf") + with open(elf_path, "rb") as f: + entry_point = await core_mini_axi.load_elf(f) + sew_addr = core_mini_axi.lookup_symbol(f, "sew") + lmul_addr = core_mini_axi.lookup_symbol(f, "lmul") + vl_addr = core_mini_axi.lookup_symbol(f, "vl") + vtype_addr = core_mini_axi.lookup_symbol(f, "vtype") + result_vl_addr = core_mini_axi.lookup_symbol(f, "result_vl") + + cases = [ + (0b000, 0b110, 4), # SEW8, mf4, vlmax=4 + (0b000, 0b111, 8), # SEW8, mf2, vlmax=8 + (0b000, 0b000, 16), # SEW8, m1, vlmax=16 + (0b000, 0b001, 32), # SEW8, m2, vlmax=32 + (0b000, 0b010, 64), # SEW8, m4, vlmax=64 + (0b000, 0b011, 128), # SEW8, m8, vlmax=128 + (0b001, 0b111, 4), # SEW16, mf2, vlmax=4 + (0b001, 0b000, 8), # SEW16, m1, vlmax=8 + (0b001, 0b001, 16), # SEW16, m2, vlmax=16 + (0b001, 0b010, 32), # SEW16, m4, vlmax=32 + (0b001, 0b011, 64), # SEW16, m8, vlmax=64 + (0b010, 0b000, 4), # SEW32, m1, vlmax=4 + (0b010, 0b001, 8), # SEW32, m2, vlmax=8 + (0b010, 0b010, 16), # SEW32, m4, vlmax=16 + (0b010, 0b011, 32), # SEW32, m8, vlmax=32 + ] + for sew, lmul, vlmax in tqdm.tqdm(cases): + await core_mini_axi.write_word(sew_addr, sew) + await core_mini_axi.write_word(lmul_addr, lmul) + + # Test saturation above vlmax + vl_to_set = vlmax + 1 + await core_mini_axi.write_word(vl_addr, vl_to_set) + await core_mini_axi.execute_from(entry_point) + await core_mini_axi.wait_for_halted() + vl_result = ( + await core_mini_axi.read_word(result_vl_addr)).view(np.uint32)[0] + assert(vl_result == vlmax) + + # Test vlmax + await core_mini_axi.write_word(vl_addr, vlmax) + await core_mini_axi.execute_from(entry_point) + await core_mini_axi.wait_for_halted() + vl_result = ( + await core_mini_axi.read_word(result_vl_addr)).view(np.uint32)[0] + assert(vl_result == vlmax) + + # Test below vlmax + await core_mini_axi.write_word(vl_addr, vlmax - 1) + await core_mini_axi.execute_from(entry_point) + await core_mini_axi.wait_for_halted() + vl_result = ( + await core_mini_axi.read_word(result_vl_addr)).view(np.uint32)[0] + assert(vl_result == (vlmax - 1)) \ No newline at end of file
diff --git a/tests/cocotb/rvv_load_store_test.py b/tests/cocotb/rvv_load_store_test.py index ea9b0b4..900d336 100644 --- a/tests/cocotb/rvv_load_store_test.py +++ b/tests/cocotb/rvv_load_store_test.py
@@ -1332,81 +1332,81 @@ make_test_case('vluxseg2ei8_v_u16m4x2', vl=31, n_segs=2), # Unordered, segment 3 make_test_case('vluxseg3ei8_v_u16mf2x3', vl=4, n_segs=3), - make_test_case('vluxseg3ei8_v_u16mf2x3', vl=5, n_segs=3), - make_test_case('vluxseg3ei8_v_u16m1x3', vl=7, n_segs=3), + make_test_case('vluxseg3ei8_v_u16mf2x3', vl=3, n_segs=3), make_test_case('vluxseg3ei8_v_u16m1x3', vl=8, n_segs=3), + make_test_case('vluxseg3ei8_v_u16m1x3', vl=7, n_segs=3), make_test_case('vluxseg3ei8_v_u16m2x3', vl=16, n_segs=3), make_test_case('vluxseg3ei8_v_u16m2x3', vl=15, n_segs=3), # Unordered, segment 4 make_test_case('vluxseg4ei8_v_u16mf2x4', vl=4, n_segs=4), - make_test_case('vluxseg4ei8_v_u16mf2x4', vl=5, n_segs=4), - make_test_case('vluxseg4ei8_v_u16m1x4', vl=7, n_segs=4), + make_test_case('vluxseg4ei8_v_u16mf2x4', vl=3, n_segs=4), make_test_case('vluxseg4ei8_v_u16m1x4', vl=8, n_segs=4), + make_test_case('vluxseg4ei8_v_u16m1x4', vl=7, n_segs=4), make_test_case('vluxseg4ei8_v_u16m2x4', vl=16, n_segs=4), make_test_case('vluxseg4ei8_v_u16m2x4', vl=15, n_segs=4), # Unordered, segment 5 make_test_case('vluxseg5ei8_v_u16mf2x5', vl=4, n_segs=5), - make_test_case('vluxseg5ei8_v_u16mf2x5', vl=5, n_segs=5), - make_test_case('vluxseg5ei8_v_u16m1x5', vl=7, n_segs=5), + make_test_case('vluxseg5ei8_v_u16mf2x5', vl=3, n_segs=5), make_test_case('vluxseg5ei8_v_u16m1x5', vl=8, n_segs=5), + make_test_case('vluxseg5ei8_v_u16m1x5', vl=7, n_segs=5), # Unordered, segment 6 make_test_case('vluxseg6ei8_v_u16mf2x6', vl=4, n_segs=6), - make_test_case('vluxseg6ei8_v_u16mf2x6', vl=5, n_segs=6), - make_test_case('vluxseg6ei8_v_u16m1x6', vl=7, n_segs=6), + make_test_case('vluxseg6ei8_v_u16mf2x6', vl=3, n_segs=6), make_test_case('vluxseg6ei8_v_u16m1x6', vl=8, n_segs=6), + make_test_case('vluxseg6ei8_v_u16m1x6', vl=7, n_segs=6), # Unordered, segment 7 make_test_case('vluxseg7ei8_v_u16mf2x7', vl=4, n_segs=7), - make_test_case('vluxseg7ei8_v_u16mf2x7', vl=5, n_segs=7), - make_test_case('vluxseg7ei8_v_u16m1x7', vl=7, n_segs=7), + make_test_case('vluxseg7ei8_v_u16mf2x7', vl=3, n_segs=7), make_test_case('vluxseg7ei8_v_u16m1x7', vl=8, n_segs=7), + make_test_case('vluxseg7ei8_v_u16m1x7', vl=7, n_segs=7), # Unordered, segment 8 make_test_case('vluxseg8ei8_v_u16mf2x8', vl=4, n_segs=8), - make_test_case('vluxseg8ei8_v_u16mf2x8', vl=5, n_segs=8), - make_test_case('vluxseg8ei8_v_u16m1x8', vl=7, n_segs=8), + make_test_case('vluxseg8ei8_v_u16mf2x8', vl=3, n_segs=8), make_test_case('vluxseg8ei8_v_u16m1x8', vl=8, n_segs=8), + make_test_case('vluxseg8ei8_v_u16m1x8', vl=7, n_segs=8), # Ordered, segment 2 make_test_case('vloxseg2ei8_v_u16mf2x2', vl=4, n_segs=2), - make_test_case('vloxseg2ei8_v_u16mf2x2', vl=5, n_segs=2), - make_test_case('vloxseg2ei8_v_u16m1x2', vl=7, n_segs=2), + make_test_case('vloxseg2ei8_v_u16mf2x2', vl=3, n_segs=2), make_test_case('vloxseg2ei8_v_u16m1x2', vl=8, n_segs=2), + make_test_case('vloxseg2ei8_v_u16m1x2', vl=7, n_segs=2), make_test_case('vloxseg2ei8_v_u16m2x2', vl=16, n_segs=2), make_test_case('vloxseg2ei8_v_u16m2x2', vl=15, n_segs=2), make_test_case('vloxseg2ei8_v_u16m4x2', vl=32, n_segs=2), make_test_case('vloxseg2ei8_v_u16m4x2', vl=31, n_segs=2), # Ordered, segment 3 make_test_case('vloxseg3ei8_v_u16mf2x3', vl=4, n_segs=3), - make_test_case('vloxseg3ei8_v_u16mf2x3', vl=5, n_segs=3), - make_test_case('vloxseg3ei8_v_u16m1x3', vl=7, n_segs=3), + make_test_case('vloxseg3ei8_v_u16mf2x3', vl=3, n_segs=3), make_test_case('vloxseg3ei8_v_u16m1x3', vl=8, n_segs=3), + make_test_case('vloxseg3ei8_v_u16m1x3', vl=7, n_segs=3), make_test_case('vloxseg3ei8_v_u16m2x3', vl=16, n_segs=3), make_test_case('vloxseg3ei8_v_u16m2x3', vl=15, n_segs=3), # Ordered, segment 4 make_test_case('vloxseg4ei8_v_u16mf2x4', vl=4, n_segs=4), - make_test_case('vloxseg4ei8_v_u16mf2x4', vl=5, n_segs=4), - make_test_case('vloxseg4ei8_v_u16m1x4', vl=7, n_segs=4), + make_test_case('vloxseg4ei8_v_u16mf2x4', vl=3, n_segs=4), make_test_case('vloxseg4ei8_v_u16m1x4', vl=8, n_segs=4), + make_test_case('vloxseg4ei8_v_u16m1x4', vl=7, n_segs=4), make_test_case('vloxseg4ei8_v_u16m2x4', vl=16, n_segs=4), make_test_case('vloxseg4ei8_v_u16m2x4', vl=15, n_segs=4), # Ordered, segment 5 make_test_case('vloxseg5ei8_v_u16mf2x5', vl=4, n_segs=5), - make_test_case('vloxseg5ei8_v_u16mf2x5', vl=5, n_segs=5), - make_test_case('vloxseg5ei8_v_u16m1x5', vl=7, n_segs=5), + make_test_case('vloxseg5ei8_v_u16mf2x5', vl=3, n_segs=5), make_test_case('vloxseg5ei8_v_u16m1x5', vl=8, n_segs=5), + make_test_case('vloxseg5ei8_v_u16m1x5', vl=7, n_segs=5), # Ordered, segment 6 make_test_case('vloxseg6ei8_v_u16mf2x6', vl=4, n_segs=6), - make_test_case('vloxseg6ei8_v_u16mf2x6', vl=5, n_segs=6), - make_test_case('vloxseg6ei8_v_u16m1x6', vl=7, n_segs=6), + make_test_case('vloxseg6ei8_v_u16mf2x6', vl=3, n_segs=6), make_test_case('vloxseg6ei8_v_u16m1x6', vl=8, n_segs=6), + make_test_case('vloxseg6ei8_v_u16m1x6', vl=7, n_segs=6), # Ordered, segment 7 make_test_case('vloxseg7ei8_v_u16mf2x7', vl=4, n_segs=7), - make_test_case('vloxseg7ei8_v_u16mf2x7', vl=5, n_segs=7), - make_test_case('vloxseg7ei8_v_u16m1x7', vl=7, n_segs=7), + make_test_case('vloxseg7ei8_v_u16mf2x7', vl=3, n_segs=7), make_test_case('vloxseg7ei8_v_u16m1x7', vl=8, n_segs=7), + make_test_case('vloxseg7ei8_v_u16m1x7', vl=7, n_segs=7), # Ordered, segment 8 make_test_case('vloxseg8ei8_v_u16mf2x8', vl=4, n_segs=8), - make_test_case('vloxseg8ei8_v_u16mf2x8', vl=5, n_segs=8), - make_test_case('vloxseg8ei8_v_u16m1x8', vl=7, n_segs=8), + make_test_case('vloxseg8ei8_v_u16mf2x8', vl=3, n_segs=8), make_test_case('vloxseg8ei8_v_u16m1x8', vl=8, n_segs=8), + make_test_case('vloxseg8ei8_v_u16m1x8', vl=7, n_segs=8), ], dtype = np.uint16, index_dtype = np.uint8,