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,