From 0cb6b4f04da67146ebc9edf8ab9996eb8c01d95b Mon Sep 17 00:00:00 2001 From: Xinlai Wan Date: Tue, 27 Dec 2022 20:23:10 +0800 Subject: [PATCH] These updates introduce support for the RISC-V Vector Extension. It is a combination of 16 commits. V extension general framework and configuration setting instructions (#191) * V extension general framework and configuration setting instructions * Update model/riscv_insts_vext_utils.sail fix a typo Co-authored-by: Nicolas Brunie Signed-off-by: BrighterW * Update model/riscv_insts_vext_vset.sail * Revisions after Nov 22 meeting * Update effect matching for functions in riscv_vlen.sail * Fix code formatting issues * Update model/riscv_insts_vext_utils.sail Co-authored-by: Jessica Clarke Signed-off-by: Xinlai Wan * Fix coding style issues * Update vset instructions Signed-off-by: BrighterW Signed-off-by: Xinlai Wan Co-authored-by: Nicolas Brunie Co-authored-by: Jessica Clarke Vector load / store instructions (#198) * Add vector load / store instructions * Modify the implementation of SEW, LMUL, VLEN and avoid real numbers in the code * Update vstart setting in vector load / store instructions * Remove unnecessary assert statements in vector instructions * Fix bugs in vleff instructions and revise coding styles * Add guards for vector encdec clauses, Avoid redundant memory access after vector load/store failure Vector integer/fixed-point arithmetic & mask instructions (#227) * Add vector arithmetic & mask instructions * Update vector EEW and EMUL checking function * Add vector instruction illegal check functions * Adjust code formatting for vector instruction illegal check functions Merge approved by team at tech-golden-model meeting on 2023-03-14. Vector floating-point instructions (#232) * Add vector floating-point instructions * Update vector floating-point conversion instructions * Update copyright headers for vector extension code --------- Co-authored-by: xwan Vector reduction and mask instructions (#259) * Add vector mask and reduction instructions * Fix register overlap check in vector mask instructions --------- Co-authored-by: xwan update vector CSR vtype.vill setting and judgement Summarize patterns of vector illegal instruction check Fix issues in vector load/store and reduction operations Clean up the V extension code and vector floating-point functions Clean up the softfloat makefiles Rename EXTZ and EXTS in the V extension code Fix an issue in the V extension code for clang-format check Fix NaN boxing issue and add notes for RVV configuration TODOs Add default VLEN value and set vlenb CSR Add constraints for vector variable initialization Add mstatus.VS setting code for vector extension --- Makefile | 14 +- .../build/Linux-RISCV-GCC/Makefile | 1 + c_emulator/riscv_platform.c | 5 + c_emulator/riscv_platform.h | 1 + c_emulator/riscv_platform_impl.c | 1 + c_emulator/riscv_platform_impl.h | 1 + c_emulator/riscv_sim.c | 5 + handwritten_support/riscv_extras_fdext.lem | 9 + model/prelude.sail | 28 +- model/riscv_csr_map.sail | 8 + model/riscv_insts_vext_arith.sail | 2331 +++++++++++++++++ model/riscv_insts_vext_fp.sail | 1363 ++++++++++ model/riscv_insts_vext_mask.sail | 388 +++ model/riscv_insts_vext_mem.sail | 939 +++++++ model/riscv_insts_vext_red.sail | 288 ++ model/riscv_insts_vext_utils.sail | 1145 ++++++++ model/riscv_insts_vext_vm.sail | 883 +++++++ model/riscv_insts_vext_vset.sail | 213 ++ model/riscv_insts_zicsr.sail | 18 + model/riscv_softfloat_interface.sail | 1 + model/riscv_sys_control.sail | 33 +- model/riscv_sys_regs.sail | 99 +- model/riscv_vext_control.sail | 58 + model/riscv_vext_regs.sail | 463 ++++ model/riscv_vlen.sail | 83 + model/riscv_vreg_type.sail | 179 ++ ocaml_emulator/platform.ml | 2 + ocaml_emulator/riscv_ocaml_sim.ml | 3 + 28 files changed, 8551 insertions(+), 11 deletions(-) create mode 100644 model/riscv_insts_vext_arith.sail create mode 100755 model/riscv_insts_vext_fp.sail create mode 100755 model/riscv_insts_vext_mask.sail create mode 100644 model/riscv_insts_vext_mem.sail create mode 100755 model/riscv_insts_vext_red.sail create mode 100755 model/riscv_insts_vext_utils.sail create mode 100755 model/riscv_insts_vext_vm.sail create mode 100644 model/riscv_insts_vext_vset.sail create mode 100755 model/riscv_vext_control.sail create mode 100644 model/riscv_vext_regs.sail create mode 100644 model/riscv_vlen.sail create mode 100755 model/riscv_vreg_type.sail diff --git a/Makefile b/Makefile index 3198db7ee..9140065bd 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ else endif SAIL_FLEN := riscv_flen_D.sail +SAIL_VLEN := riscv_vlen.sail # Instruction sources, depending on target SAIL_CHECK_SRCS = riscv_addr_checks_common.sail riscv_addr_checks.sail riscv_misa_ext.sail @@ -41,6 +42,15 @@ SAIL_DEFAULT_INST += riscv_insts_zbkx.sail SAIL_DEFAULT_INST += riscv_insts_zicond.sail +SAIL_DEFAULT_INST += riscv_insts_vext_utils.sail +SAIL_DEFAULT_INST += riscv_insts_vext_vset.sail +SAIL_DEFAULT_INST += riscv_insts_vext_arith.sail +SAIL_DEFAULT_INST += riscv_insts_vext_fp.sail +SAIL_DEFAULT_INST += riscv_insts_vext_mem.sail +SAIL_DEFAULT_INST += riscv_insts_vext_mask.sail +SAIL_DEFAULT_INST += riscv_insts_vext_vm.sail +SAIL_DEFAULT_INST += riscv_insts_vext_red.sail + SAIL_SEQ_INST = $(SAIL_DEFAULT_INST) riscv_jalr_seq.sail SAIL_RMEM_INST = $(SAIL_DEFAULT_INST) riscv_jalr_rmem.sail riscv_insts_rmem.sail @@ -49,6 +59,7 @@ SAIL_RMEM_INST_SRCS = riscv_insts_begin.sail $(SAIL_RMEM_INST) riscv_insts_end.s # System and platform sources SAIL_SYS_SRCS = riscv_csr_map.sail +SAIL_SYS_SRCS += riscv_vext_control.sail # helpers for the 'V' extension SAIL_SYS_SRCS += riscv_next_regs.sail SAIL_SYS_SRCS += riscv_sys_exceptions.sail # default basic helpers for exception handling SAIL_SYS_SRCS += riscv_sync_exception.sail # define the exception structure used in the model @@ -68,11 +79,12 @@ SAIL_VM_SRCS += $(SAIL_RV64_VM_SRCS) endif # Non-instruction sources -PRELUDE = prelude.sail prelude_mapping.sail $(SAIL_XLEN) $(SAIL_FLEN) prelude_mem_metadata.sail prelude_mem.sail +PRELUDE = prelude.sail prelude_mapping.sail $(SAIL_XLEN) $(SAIL_FLEN) $(SAIL_VLEN) prelude_mem_metadata.sail prelude_mem.sail SAIL_REGS_SRCS = riscv_reg_type.sail riscv_freg_type.sail riscv_regs.sail riscv_pc_access.sail riscv_sys_regs.sail SAIL_REGS_SRCS += riscv_pmp_regs.sail riscv_pmp_control.sail SAIL_REGS_SRCS += riscv_ext_regs.sail $(SAIL_CHECK_SRCS) +SAIL_REGS_SRCS += riscv_vreg_type.sail riscv_vext_regs.sail SAIL_ARCH_SRCS = $(PRELUDE) SAIL_ARCH_SRCS += riscv_types_common.sail riscv_types_ext.sail riscv_types.sail diff --git a/c_emulator/SoftFloat-3e/build/Linux-RISCV-GCC/Makefile b/c_emulator/SoftFloat-3e/build/Linux-RISCV-GCC/Makefile index 9ec18f78f..c8965c8b6 100644 --- a/c_emulator/SoftFloat-3e/build/Linux-RISCV-GCC/Makefile +++ b/c_emulator/SoftFloat-3e/build/Linux-RISCV-GCC/Makefile @@ -38,6 +38,7 @@ SOURCE_DIR ?= ../../source SPECIALIZE_TYPE ?= RISCV SOFTFLOAT_OPTS ?= \ + -DSOFTFLOAT_ROUND_ODD DELETE = rm -f diff --git a/c_emulator/riscv_platform.c b/c_emulator/riscv_platform.c index 917a36a95..35d5e4f46 100644 --- a/c_emulator/riscv_platform.c +++ b/c_emulator/riscv_platform.c @@ -37,6 +37,11 @@ bool sys_enable_zfinx(unit u) return rv_enable_zfinx; } +bool sys_enable_vext(unit u) +{ + return rv_enable_vext; +} + bool sys_enable_writable_misa(unit u) { return rv_enable_writable_misa; diff --git a/c_emulator/riscv_platform.h b/c_emulator/riscv_platform.h index aec59d017..2a4e8ba9d 100644 --- a/c_emulator/riscv_platform.h +++ b/c_emulator/riscv_platform.h @@ -6,6 +6,7 @@ bool sys_enable_next(unit); bool sys_enable_fdext(unit); bool sys_enable_zfinx(unit); bool sys_enable_writable_misa(unit); +bool sys_enable_vext(unit); bool plat_enable_dirty_update(unit); bool plat_enable_misaligned_access(unit); diff --git a/c_emulator/riscv_platform_impl.c b/c_emulator/riscv_platform_impl.c index 148c72bd4..e49b5caae 100644 --- a/c_emulator/riscv_platform_impl.c +++ b/c_emulator/riscv_platform_impl.c @@ -9,6 +9,7 @@ bool rv_enable_rvc = true; bool rv_enable_next = false; bool rv_enable_writable_misa = true; bool rv_enable_fdext = true; +bool rv_enable_vext = true; bool rv_enable_dirty_update = false; bool rv_enable_misaligned = false; diff --git a/c_emulator/riscv_platform_impl.h b/c_emulator/riscv_platform_impl.h index d24ecc8fb..660bb015a 100644 --- a/c_emulator/riscv_platform_impl.h +++ b/c_emulator/riscv_platform_impl.h @@ -12,6 +12,7 @@ extern bool rv_enable_zfinx; extern bool rv_enable_rvc; extern bool rv_enable_next; extern bool rv_enable_fdext; +extern bool rv_enable_vext; extern bool rv_enable_writable_misa; extern bool rv_enable_dirty_update; extern bool rv_enable_misaligned; diff --git a/c_emulator/riscv_sim.c b/c_emulator/riscv_sim.c index fdb9ce01d..b806402e9 100644 --- a/c_emulator/riscv_sim.c +++ b/c_emulator/riscv_sim.c @@ -231,6 +231,7 @@ char *process_args(int argc, char **argv) "N" "I" "F" + "W" "i" "s" "p" @@ -285,6 +286,10 @@ char *process_args(int argc, char **argv) fprintf(stderr, "disabling floating point (F and D extensions).\n"); rv_enable_fdext = false; break; + case 'W': + fprintf(stderr, "disabling RVV vector instructions.\n"); + rv_enable_vext = false; + break; case 'i': fprintf(stderr, "enabling storing illegal instruction bits in mtval.\n"); rv_mtval_has_illegal_inst_bits = true; diff --git a/handwritten_support/riscv_extras_fdext.lem b/handwritten_support/riscv_extras_fdext.lem index b91e5713e..84e76ee47 100644 --- a/handwritten_support/riscv_extras_fdext.lem +++ b/handwritten_support/riscv_extras_fdext.lem @@ -234,6 +234,9 @@ let softfloat_f64_to_f32 _ _ = () val softfloat_f16_lt : forall 's. Size 's => bitvector 's -> bitvector 's -> unit let softfloat_f16_lt _ _ = () +val softfloat_f16_lt_quiet : forall 's. Size 's => bitvector 's -> bitvector 's -> unit +let softfloat_f16_lt_quiet _ _ = () + val softfloat_f16_le : forall 's. Size 's => bitvector 's -> bitvector 's -> unit let softfloat_f16_le _ _ = () @@ -243,6 +246,9 @@ let softfloat_f16_eq _ _ = () val softfloat_f32_lt : forall 's. Size 's => bitvector 's -> bitvector 's -> unit let softfloat_f32_lt _ _ = () +val softfloat_f32_lt_quiet : forall 's. Size 's => bitvector 's -> bitvector 's -> unit +let softfloat_f32_lt_quiet _ _ = () + val softfloat_f32_le : forall 's. Size 's => bitvector 's -> bitvector 's -> unit let softfloat_f32_le _ _ = () @@ -252,6 +258,9 @@ let softfloat_f32_eq _ _ = () val softfloat_f64_lt : forall 's. Size 's => bitvector 's -> bitvector 's -> unit let softfloat_f64_lt _ _ = () +val softfloat_f64_lt_quiet : forall 's. Size 's => bitvector 's -> bitvector 's -> unit +let softfloat_f64_lt_quiet _ _ = () + val softfloat_f64_le : forall 's. Size 's => bitvector 's -> bitvector 's -> unit let softfloat_f64_le _ _ = () diff --git a/model/prelude.sail b/model/prelude.sail index 5ff8a1213..b86742d52 100644 --- a/model/prelude.sail +++ b/model/prelude.sail @@ -200,8 +200,11 @@ overload zeros = {zeros_implicit} val ones : forall 'n, 'n >= 0 . implicit('n) -> bits('n) function ones (n) = sail_ones (n) +val bool_to_bit : bool -> bit +function bool_to_bit x = if x then bitone else bitzero + val bool_to_bits : bool -> bits(1) -function bool_to_bits x = if x then 0b1 else 0b0 +function bool_to_bits x = [bool_to_bit(x)] val bit_to_bool : bit -> bool function bit_to_bool b = match b { @@ -329,3 +332,26 @@ val def_spc_backwards : string -> unit function def_spc_backwards s = () val def_spc_matches_prefix : string -> option((unit, nat)) function def_spc_matches_prefix s = opt_spc_matches_prefix(s) + +val "print_int" : (string, int) -> unit + +overload operator / = {quot_round_zero} +overload operator * = {mult_atom, mult_int} + +/* helper for vector extension + * 1. EEW between 8 and 64 + * 2. EMUL in vmvr.v instructions between 1 and 8 + */ +val log2 : forall 'n, 'n in {1, 2, 4, 8, 16, 32, 64}. int('n) -> int +function log2(n) = { + let result : int = match n { + 1 => 0, + 2 => 1, + 4 => 2, + 8 => 3, + 16 => 4, + 32 => 5, + 64 => 6 + }; + result +} diff --git a/model/riscv_csr_map.sail b/model/riscv_csr_map.sail index e3f8f5bb8..cd4a60604 100644 --- a/model/riscv_csr_map.sail +++ b/model/riscv_csr_map.sail @@ -165,6 +165,14 @@ mapping clause csr_name_map = 0x7a0 <-> "tselect" mapping clause csr_name_map = 0x7a1 <-> "tdata1" mapping clause csr_name_map = 0x7a2 <-> "tdata2" mapping clause csr_name_map = 0x7a3 <-> "tdata3" +/* vector csrs */ +mapping clause csr_name_map = 0x008 <-> "vstart" +mapping clause csr_name_map = 0x009 <-> "vxsat" +mapping clause csr_name_map = 0x00A <-> "vxrm" +mapping clause csr_name_map = 0x00F <-> "vcsr" +mapping clause csr_name_map = 0xC20 <-> "vl" +mapping clause csr_name_map = 0xC21 <-> "vtype" +mapping clause csr_name_map = 0xC22 <-> "vlenb" val csr_name : csreg -> string overload to_str = {csr_name} diff --git a/model/riscv_insts_vext_arith.sail b/model/riscv_insts_vext_arith.sail new file mode 100644 index 000000000..3183bb0a4 --- /dev/null +++ b/model/riscv_insts_vext_arith.sail @@ -0,0 +1,2331 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements part of the vector extension. */ +/* Chapter 11: Vector Integer Arithmetic Instructions */ +/* Chapter 12: Vector Fixed-Point Arithmetic Instructions */ +/* Chapter 16: Vector Permutation Instructions (integer part) */ +/* ******************************************************************************* */ + +/* ******************************* OPIVV (VVTYPE) ******************************** */ +union clause ast = VVTYPE : (vvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_vvfunct6 : vvfunct6 <-> bits(6) = { + VV_VADD <-> 0b000000, + VV_VSUB <-> 0b000010, + VV_VMINU <-> 0b000100, + VV_VMIN <-> 0b000101, + VV_VMAXU <-> 0b000110, + VV_VMAX <-> 0b000111, + VV_VAND <-> 0b001001, + VV_VOR <-> 0b001010, + VV_VXOR <-> 0b001011, + VV_VRGATHER <-> 0b001100, + VV_VRGATHEREI16 <-> 0b001110, + VV_VSADDU <-> 0b100000, + VV_VSADD <-> 0b100001, + VV_VSSUBU <-> 0b100010, + VV_VSSUB <-> 0b100011, + VV_VSLL <-> 0b100101, + VV_VSMUL <-> 0b100111, + VV_VSRL <-> 0b101000, + VV_VSRA <-> 0b101001, + VV_VSSRL <-> 0b101010, + VV_VSSRA <-> 0b101011 +} + +mapping clause encdec = VVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_vvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW_pow = get_sew_pow(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let VLEN_pow = get_vlen_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VV_VADD => vs2_val[i] + vs1_val[i], + VV_VSUB => vs2_val[i] - vs1_val[i], + VV_VAND => vs2_val[i] & vs1_val[i], + VV_VOR => vs2_val[i] | vs1_val[i], + VV_VXOR => vs2_val[i] ^ vs1_val[i], + VV_VSADDU => unsigned_saturation('m, zero_extend('m + 1, vs2_val[i]) + zero_extend('m + 1, vs1_val[i])), + VV_VSADD => signed_saturation('m, sign_extend('m + 1, vs2_val[i]) + sign_extend('m + 1, vs1_val[i])), + VV_VSSUBU => { + if unsigned(vs2_val[i]) < unsigned(vs1_val[i]) then zeros() + else unsigned_saturation('m, zero_extend('m + 1, vs2_val[i]) - zero_extend('m + 1, vs1_val[i])) + }, + VV_VSSUB => signed_saturation('m, sign_extend('m + 1, vs2_val[i]) - sign_extend('m + 1, vs1_val[i])), + VV_VSMUL => { + let result_mul = to_bits('m * 2, signed(vs2_val[i]) * signed(vs1_val[i])); + let rounding_incr = get_fixed_rounding_incr(result_mul, 'm - 1); + let result_wide = (result_mul >> ('m - 1)) + zero_extend('m * 2, rounding_incr); + signed_saturation('m, result_wide['m..0]) + }, + VV_VSLL => { + let shift_amount = get_shift_amount(vs1_val[i], SEW); + vs2_val[i] << shift_amount + }, + VV_VSRL => { + let shift_amount = get_shift_amount(vs1_val[i], SEW); + vs2_val[i] >> shift_amount + }, + VV_VSRA => { + let shift_amount = get_shift_amount(vs1_val[i], SEW); + let v_double : bits('m * 2) = sign_extend(vs2_val[i]); + slice(v_double >> shift_amount, 0, SEW) + }, + VV_VSSRL => { + let shift_amount = get_shift_amount(vs1_val[i], SEW); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + (vs2_val[i] >> shift_amount) + zero_extend('m, rounding_incr) + }, + VV_VSSRA => { + let shift_amount = get_shift_amount(vs1_val[i], SEW); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + let v_double : bits('m * 2) = sign_extend(vs2_val[i]); + slice(v_double >> shift_amount, 0, SEW) + zero_extend('m, rounding_incr) + }, + VV_VMINU => to_bits(SEW, min(unsigned(vs2_val[i]), unsigned(vs1_val[i]))), + VV_VMIN => to_bits(SEW, min(signed(vs2_val[i]), signed(vs1_val[i]))), + VV_VMAXU => to_bits(SEW, max(unsigned(vs2_val[i]), unsigned(vs1_val[i]))), + VV_VMAX => to_bits(SEW, max(signed(vs2_val[i]), signed(vs1_val[i]))), + VV_VRGATHER => { + if (vs1 == vd | vs2 == vd) then { handle_illegal(); return RETIRE_FAIL }; + let idx = unsigned(vs1_val[i]); + let VLMAX = int_power(2, LMUL_pow + VLEN_pow - SEW_pow); + assert(VLMAX <= 'n); + if idx < VLMAX then vs2_val[idx] else zeros() + }, + VV_VRGATHEREI16 => { + if (vs1 == vd | vs2 == vd) then { handle_illegal(); return RETIRE_FAIL }; + /* vrgatherei16.vv uses SEW/LMUL for the data in vs2 but EEW=16 and EMUL = (16/SEW)*LMUL for the indices in vs1 */ + let vs1_new : vector('n, dec, bits(16)) = read_vreg(num_elem, 16, 4 + LMUL_pow - SEW_pow, vs1); + let idx = unsigned(vs1_new[i]); + let VLMAX = int_power(2, LMUL_pow + VLEN_pow - SEW_pow); + assert(VLMAX <= 'n); + if idx < VLMAX then vs2_val[idx] else zeros() + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vvtype_mnemonic : vvfunct6 <-> string = { + VV_VADD <-> "vadd.vv", + VV_VSUB <-> "vsub.vv", + VV_VAND <-> "vand.vv", + VV_VOR <-> "vor.vv", + VV_VXOR <-> "vxor.vv", + VV_VRGATHER <-> "vrgather.vv", + VV_VRGATHEREI16 <-> "vrgatherei16.vv", + VV_VSADDU <-> "vsaddu.vv", + VV_VSADD <-> "vsadd.vv", + VV_VSSUBU <-> "vssubu.vv", + VV_VSSUB <-> "vssub.vv", + VV_VSLL <-> "vsll.vv", + VV_VSMUL <-> "vsmul.vv", + VV_VSRL <-> "vsrl.vv", + VV_VSRA <-> "vsra.vv", + VV_VSSRL <-> "vssrl.vv", + VV_VSSRA <-> "vssra.vv", + VV_VMINU <-> "vminu.vv", + VV_VMIN <-> "vmin.vv", + VV_VMAXU <-> "vmaxu.vv", + VV_VMAX <-> "vmax.vv" +} + +mapping clause assembly = VVTYPE(funct6, vm, vs2, vs1, vd) + <-> vvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ************************** OPIVV (WVTYPE Narrowing) *************************** */ +/* ************** Vector Narrowing Integer Right Shift Instructions ************** */ +union clause ast = NVSTYPE : (nvsfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_nvsfunct6 : nvsfunct6 <-> bits(6) = { + NVS_VNSRL <-> 0b101100, + NVS_VNSRA <-> 0b101101 +} + +mapping clause encdec = NVSTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_nvsfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(NVSTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_widen, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW_widen <= 64); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + NVS_VNSRL => { + let shift_amount = get_shift_amount(vs1_val[i], SEW_widen); + slice(vs2_val[i] >> shift_amount, 0, SEW) + }, + NVS_VNSRA => { + let shift_amount = get_shift_amount(vs1_val[i], SEW_widen); + let v_double : bits('o * 2) = sign_extend(vs2_val[i]); + let arith_shifted : bits('o) = slice(v_double >> shift_amount, 0, SEW_widen); + slice(arith_shifted, 0, SEW) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping nvstype_mnemonic : nvsfunct6 <-> string = { + NVS_VNSRL <-> "vnsrl.wv", + NVS_VNSRA <-> "vnsra.wv" +} + +mapping clause assembly = NVSTYPE(funct6, vm, vs2, vs1, vd) + <-> nvstype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ************************** OPIVV (WVTYPE Narrowing) *************************** */ +/* *************** Vector Narrowing Fixed-Point Clip Instructions **************** */ +union clause ast = NVTYPE : (nvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_nvfunct6 : nvfunct6 <-> bits(6) = { + NV_VNCLIPU <-> 0b101110, + NV_VNCLIP <-> 0b101111 +} + +mapping clause encdec = NVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_nvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(NVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_widen, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW_widen <= 64); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let shift_amount = get_shift_amount(vs1_val[i], SEW_widen); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + result[i] = match funct6 { + NV_VNCLIPU => { + let result_wide = (vs2_val[i] >> shift_amount) + zero_extend('o, rounding_incr); + unsigned_saturation('m, result_wide); + }, + NV_VNCLIP => { + let v_double : bits('m * 4) = sign_extend(vs2_val[i]); + let result_wide = slice(v_double >> shift_amount, 0, 'o) + zero_extend('o, rounding_incr); + signed_saturation('m, result_wide); + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping nvtype_mnemonic : nvfunct6 <-> string = { + NV_VNCLIPU <-> "vnclipu.wv", + NV_VNCLIP <-> "vnclip.wv" +} + +mapping clause assembly = NVTYPE(funct6, vm, vs2, vs1, vd) + <-> nvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ********************** OPIVV (Integer Merge Instruction) ********************** */ +union clause ast = MASKTYPEV : (regidx, regidx, regidx) + +mapping clause encdec = MASKTYPEV (vs2, vs1, vd) if haveVExt() + <-> 0b010111 @ 0b0 @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MASKTYPEV(vs2, vs1, vd)) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); /* max(VLMAX,VLEN/SEW)) */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / (0 - LMUL_pow); /* VLMAX */ + + if illegal_vd_masked(vd) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + + let tail_ag : agtype = get_vtype_vta(); + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + result[i] = vd_val[i] + } else if i > end_element | i >= real_num_elem then { + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + } + } else { + /* the merge operates on all body elements */ + result[i] = if vm_val[i] then vs1_val[i] else vs2_val[i] + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = MASKTYPEV(vs2, vs1, vd) +<-> "vmerge.vvm" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ sep() ^ "v0" + +/* ********************** OPIVV (Integer Move Instruction) *********************** */ +union clause ast = MOVETYPEV : (regidx, regidx) + +mapping clause encdec = MOVETYPEV (vs1, vd) if haveVExt() + <-> 0b010111 @ 0b1 @ 0b00000 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MOVETYPEV(vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b1, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then result[i] = vs1_val[i] + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = MOVETYPEV(vs1, vd) + <-> "vmv.v.v" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs1) + +/* ******************************* OPIVX (VXTYPE) ******************************** */ +union clause ast = VXTYPE : (vxfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_vxfunct6 : vxfunct6 <-> bits(6) = { + VX_VADD <-> 0b000000, + VX_VSUB <-> 0b000010, + VX_VRSUB <-> 0b000011, + VX_VMINU <-> 0b000100, + VX_VMIN <-> 0b000101, + VX_VMAXU <-> 0b000110, + VX_VMAX <-> 0b000111, + VX_VAND <-> 0b001001, + VX_VOR <-> 0b001010, + VX_VXOR <-> 0b001011, + VX_VSADDU <-> 0b100000, + VX_VSADD <-> 0b100001, + VX_VSSUBU <-> 0b100010, + VX_VSSUB <-> 0b100011, + VX_VSLL <-> 0b100101, + VX_VSMUL <-> 0b100111, + VX_VSRL <-> 0b101000, + VX_VSRA <-> 0b101001, + VX_VSSRL <-> 0b101010, + VX_VSSRA <-> 0b101011 +} + +mapping clause encdec = VXTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_vxfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VXTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VX_VADD => vs2_val[i] + rs1_val, + VX_VSUB => vs2_val[i] - rs1_val, + VX_VRSUB => rs1_val - vs2_val[i], + VX_VAND => vs2_val[i] & rs1_val, + VX_VOR => vs2_val[i] | rs1_val, + VX_VXOR => vs2_val[i] ^ rs1_val, + VX_VSADDU => unsigned_saturation('m, zero_extend('m + 1, vs2_val[i]) + zero_extend('m + 1, rs1_val) ), + VX_VSADD => signed_saturation('m, sign_extend('m + 1, vs2_val[i]) + sign_extend('m + 1, rs1_val) ), + VX_VSSUBU => { + if unsigned(vs2_val[i]) < unsigned(rs1_val) then zeros() + else unsigned_saturation('m, zero_extend('m + 1, vs2_val[i]) - zero_extend('m + 1, rs1_val) ) + }, + VX_VSSUB => signed_saturation('m, sign_extend('m + 1, vs2_val[i]) - sign_extend('m + 1, rs1_val) ), + VX_VSMUL => { + let result_mul = to_bits('m * 2, signed(vs2_val[i]) * signed(rs1_val)); + let rounding_incr = get_fixed_rounding_incr(result_mul, 'm - 1); + let result_wide = (result_mul >> ('m - 1)) + zero_extend('m * 2, rounding_incr); + signed_saturation('m, result_wide['m..0]) + }, + VX_VSLL => { + let shift_amount = get_shift_amount(rs1_val, SEW); + vs2_val[i] << shift_amount + }, + VX_VSRL => { + let shift_amount = get_shift_amount(rs1_val, SEW); + vs2_val[i] >> shift_amount + }, + VX_VSRA => { + let shift_amount = get_shift_amount(rs1_val, SEW); + let v_double : bits('m * 2) = sign_extend(vs2_val[i]); + slice(v_double >> shift_amount, 0, SEW) + }, + VX_VSSRL => { + let shift_amount = get_shift_amount(rs1_val, SEW); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + (vs2_val[i] >> shift_amount) + zero_extend('m, rounding_incr) + }, + VX_VSSRA => { + let shift_amount = get_shift_amount(rs1_val, SEW); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + let v_double : bits('m * 2) = sign_extend(vs2_val[i]); + slice(v_double >> shift_amount, 0, SEW) + zero_extend('m, rounding_incr) + }, + VX_VMINU => to_bits(SEW, min(unsigned(vs2_val[i]), unsigned(rs1_val))), + VX_VMIN => to_bits(SEW, min(signed(vs2_val[i]), signed(rs1_val))), + VX_VMAXU => to_bits(SEW, max(unsigned(vs2_val[i]), unsigned(rs1_val))), + VX_VMAX => to_bits(SEW, max(signed(vs2_val[i]), signed(rs1_val))) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vxtype_mnemonic : vxfunct6 <-> string = { + VX_VADD <-> "vadd.vx", + VX_VSUB <-> "vsub.vx", + VX_VRSUB <-> "vrsub.vx", + VX_VAND <-> "vand.vx", + VX_VOR <-> "vor.vx", + VX_VXOR <-> "vxor.vx", + VX_VSADDU <-> "vsaddu.vx", + VX_VSADD <-> "vsadd.vx", + VX_VSSUBU <-> "vssubu.vx", + VX_VSSUB <-> "vssub.vx", + VX_VSLL <-> "vsll.vx", + VX_VSMUL <-> "vsmul.vx", + VX_VSRL <-> "vsrl.vx", + VX_VSRA <-> "vsra.vx", + VX_VSSRL <-> "vssrl.vx", + VX_VSSRA <-> "vssra.vx", + VX_VMINU <-> "vminu.vx", + VX_VMIN <-> "vmin.vx", + VX_VMAXU <-> "vmaxu.vx", + VX_VMAX <-> "vmax.vx" +} + +mapping clause assembly = VXTYPE(funct6, vm, vs2, rs1, vd) + <-> vxtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ************************** OPIVX (WXTYPE Narrowing) *************************** */ +/* ************** Vector Narrowing Integer Right Shift Instructions ************** */ +union clause ast = NXSTYPE : (nxsfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_nxsfunct6 : nxsfunct6 <-> bits(6) = { + NXS_VNSRL <-> 0b101100, + NXS_VNSRA <-> 0b101101 +} + +mapping clause encdec = NXSTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_nxsfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(NXSTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_widen, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW_widen <= 64); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + NXS_VNSRL => { + let shift_amount = get_shift_amount(rs1_val, SEW_widen); + slice(vs2_val[i] >> shift_amount, 0, SEW) + }, + NXS_VNSRA => { + let shift_amount = get_shift_amount(rs1_val, SEW_widen); + let v_double : bits('o * 2) = sign_extend(vs2_val[i]); + let arith_shifted : bits('o) = slice(v_double >> shift_amount, 0, SEW_widen); + slice(arith_shifted, 0, SEW) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping nxstype_mnemonic : nxsfunct6 <-> string = { + NXS_VNSRL <-> "vnsrl.wx", + NXS_VNSRA <-> "vnsra.wx" +} + +mapping clause assembly = NXSTYPE(funct6, vm, vs2, rs1, vd) + <-> nxstype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ************************** OPIVX (WXTYPE Narrowing) *************************** */ +/* *************** Vector Narrowing Fixed-Point Clip Instructions **************** */ +union clause ast = NXTYPE : (nxfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_nxfunct6 : nxfunct6 <-> bits(6) = { + NX_VNCLIPU <-> 0b101110, + NX_VNCLIP <-> 0b101111 +} + +mapping clause encdec = NXTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_nxfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(NXTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_widen, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW_widen <= 64); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let shift_amount = get_shift_amount(rs1_val, SEW_widen); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + result[i] = match funct6 { + NX_VNCLIPU => { + let result_wide = (vs2_val[i] >> shift_amount) + zero_extend('o, rounding_incr); + unsigned_saturation('m, result_wide) + }, + NX_VNCLIP => { + let v_double : bits('m * 4) = sign_extend(vs2_val[i]); + let result_wide = slice(v_double >> shift_amount, 0, 'o) + zero_extend('o, rounding_incr); + signed_saturation('m, result_wide) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping nxtype_mnemonic : nxfunct6 <-> string = { + NX_VNCLIPU <-> "vnclipu.wx", + NX_VNCLIP <-> "vnclip.wx" +} + +mapping clause assembly = NXTYPE(funct6, vm, vs2, rs1, vd) + <-> nxtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ***************** OPIVX (Vector Slide & Gather Instructions) ****************** */ +/* Slide and gather instructions extend rs1/imm to XLEN intead of SEW bits */ +union clause ast = VXSG : (vxsgfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_vxsgfunct6 : vxsgfunct6 <-> bits(6) = { + VX_VSLIDEUP <-> 0b001110, + VX_VSLIDEDOWN <-> 0b001111, + VX_VRGATHER <-> 0b001100 +} + +mapping clause encdec = VXSG(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_vxsgfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VXSG(funct6, vm, vs2, rs1, vd)) = { + let SEW_pow = get_sew_pow(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let VLEN_pow = get_vlen_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : nat = unsigned(X(rs1)); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VX_VSLIDEUP => { + if (vs2 == vd) then { handle_illegal(); return RETIRE_FAIL }; + if i >= rs1_val then vs2_val[i - rs1_val] else vd_val[i] + }, + VX_VSLIDEDOWN => { + let VLMAX = int_power(2, LMUL_pow + VLEN_pow - SEW_pow); + assert(VLMAX > 0 & VLMAX <= 'n); + if i + rs1_val < VLMAX then vs2_val[i + rs1_val] else zeros() + }, + VX_VRGATHER => { + if (vs2 == vd) then { handle_illegal(); return RETIRE_FAIL }; + let VLMAX = int_power(2, LMUL_pow + VLEN_pow - SEW_pow); + assert(VLMAX > 0 & VLMAX <= 'n); + if rs1_val < VLMAX then vs2_val[rs1_val] else zeros() + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vxsg_mnemonic : vxsgfunct6 <-> string = { + VX_VSLIDEUP <-> "vslideup.vx", + VX_VSLIDEDOWN <-> "vslidedown.vx", + VX_VRGATHER <-> "vrgather.vx" +} + +mapping clause assembly = VXSG(funct6, vm, vs2, rs1, vd) + <-> vxsg_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ********************** OPIVX (Integer Merge Instruction) ********************** */ +union clause ast = MASKTYPEX : (regidx, regidx, regidx) + +mapping clause encdec = MASKTYPEX(vs2, rs1, vd) if haveVExt() + <-> 0b010111 @ 0b0 @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MASKTYPEX(vs2, rs1, vd)) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); /* max(VLMAX,VLEN/SEW)) */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / (0 - LMUL_pow); /* VLMAX */ + + if illegal_vd_masked(vd) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + + let tail_ag : agtype = get_vtype_vta(); + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + result[i] = vd_val[i] + } else if i > end_element | i >= real_num_elem then { + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + } + } else { + /* the merge operates on all body elements */ + result[i] = if vm_val[i] then rs1_val else vs2_val[i] + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = MASKTYPEX(vs2, rs1, vd) + <-> "vmerge.vxm" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ sep() ^ "v0" + +/* ********************** OPIVX (Integer Move Instruction) *********************** */ +union clause ast = MOVETYPEX : (regidx, regidx) + +mapping clause encdec = MOVETYPEX (rs1, vd) if haveVExt() + <-> 0b010111 @ 0b1 @ 0b00000 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MOVETYPEX(rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let rs1_val : bits('m) = get_scalar(rs1, 'm); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b1, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then result[i] = rs1_val + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = MOVETYPEX(rs1, vd) + <-> "vmv.v.x" ^ spc() ^ vreg_name(vd) ^ sep() ^ reg_name(rs1) + +/* ******************************* OPIVI (VITYPE) ******************************** */ +union clause ast = VITYPE : (vifunct6, bits(1), regidx, bits(5), regidx) + +mapping encdec_vifunct6 : vifunct6 <-> bits(6) = { + VI_VADD <-> 0b000000, + VI_VRSUB <-> 0b000011, + VI_VAND <-> 0b001001, + VI_VOR <-> 0b001010, + VI_VXOR <-> 0b001011, + VI_VSADDU <-> 0b100000, + VI_VSADD <-> 0b100001, + VI_VSLL <-> 0b100101, + VI_VSRL <-> 0b101000, + VI_VSRA <-> 0b101001, + VI_VSSRL <-> 0b101010, + VI_VSSRA <-> 0b101011 +} + +mapping clause encdec = VITYPE(funct6, vm, vs2, simm, vd) if haveVExt() + <-> encdec_vifunct6(funct6) @ vm @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VITYPE(funct6, vm, vs2, simm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VI_VADD => vs2_val[i] + imm_val, + VI_VRSUB => imm_val - vs2_val[i], + VI_VAND => vs2_val[i] & imm_val, + VI_VOR => vs2_val[i] | imm_val, + VI_VXOR => vs2_val[i] ^ imm_val, + VI_VSADDU => unsigned_saturation('m, zero_extend('m + 1, vs2_val[i]) + zero_extend('m + 1, imm_val) ), + VI_VSADD => signed_saturation('m, sign_extend('m + 1, vs2_val[i]) + sign_extend('m + 1, imm_val) ), + VI_VSLL => { + let shift_amount = get_shift_amount(zero_extend('m, simm), SEW); + vs2_val[i] << shift_amount + }, + VI_VSRL => { + let shift_amount = get_shift_amount(zero_extend('m, simm), SEW); + vs2_val[i] >> shift_amount + }, + VI_VSRA => { + let shift_amount = get_shift_amount(zero_extend('m, simm), SEW); + let v_double : bits('m * 2) = sign_extend(vs2_val[i]); + slice(v_double >> shift_amount, 0, SEW) + }, + VI_VSSRL => { + let shift_amount = get_shift_amount(zero_extend('m, simm), SEW); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + (vs2_val[i] >> shift_amount) + zero_extend('m, rounding_incr) + }, + VI_VSSRA => { + let shift_amount = get_shift_amount(zero_extend('m, simm), SEW); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + let v_double : bits('m * 2) = sign_extend(vs2_val[i]); + slice(v_double >> shift_amount, 0, SEW) + zero_extend('m, rounding_incr) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vitype_mnemonic : vifunct6 <-> string = { + VI_VADD <-> "vadd.vi", + VI_VRSUB <-> "vrsub.vi", + VI_VAND <-> "vand.vi", + VI_VOR <-> "vor.vi", + VI_VXOR <-> "vxor.vi", + VI_VSADDU <-> "vsaddu.vi", + VI_VSADD <-> "vsadd.vi", + VI_VSLL <-> "vsll.vi", + VI_VSRL <-> "vsrl.vi", + VI_VSRA <-> "vsra.vi", + VI_VSSRL <-> "vssrl.vi", + VI_VSSRA <-> "vssra.vi" +} + +mapping clause assembly = VITYPE(funct6, vm, vs2, simm, vd) + <-> vitype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) ^ maybe_vmask(vm) + +/* ************************** OPIVI (WITYPE Narrowing) *************************** */ +/* ************** Vector Narrowing Integer Right Shift Instructions ************** */ +union clause ast = NISTYPE : (nisfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_nisfunct6 : nisfunct6 <-> bits(6) = { + NIS_VNSRL <-> 0b101100, + NIS_VNSRA <-> 0b101101 +} + +mapping clause encdec = NISTYPE(funct6, vm, vs2, simm, vd) if haveVExt() + <-> encdec_nisfunct6(funct6) @ vm @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(NISTYPE(funct6, vm, vs2, simm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_widen, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW_widen <= 64); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + NIS_VNSRL => { + let shift_amount = get_shift_amount(imm_val, SEW_widen); + slice(vs2_val[i] >> shift_amount, 0, SEW) + }, + NIS_VNSRA => { + let shift_amount = get_shift_amount(imm_val, SEW_widen); + let v_double : bits('o * 2) = sign_extend(vs2_val[i]); + let arith_shifted : bits('o) = slice(v_double >> shift_amount, 0, SEW_widen); + slice(arith_shifted, 0, SEW) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping nistype_mnemonic : nisfunct6 <-> string = { + NIS_VNSRL <-> "vnsrl.wi", + NIS_VNSRA <-> "vnsra.wi" +} + +mapping clause assembly = NISTYPE(funct6, vm, vs2, simm, vd) + <-> nistype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) ^ maybe_vmask(vm) + +/* ************************** OPIVI (WITYPE Narrowing) *************************** */ +/* *************** Vector Narrowing Fixed-Point Clip Instructions **************** */ +union clause ast = NITYPE : (nifunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_nifunct6 : nifunct6 <-> bits(6) = { + NI_VNCLIPU <-> 0b101110, + NI_VNCLIP <-> 0b101111 +} + +mapping clause encdec = NITYPE(funct6, vm, vs2, simm, vd) if haveVExt() + <-> encdec_nifunct6(funct6) @ vm @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(NITYPE(funct6, vm, vs2, simm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_widen, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW_widen <= 64); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let shift_amount = get_shift_amount(imm_val, SEW_widen); + let rounding_incr = get_fixed_rounding_incr(vs2_val[i], shift_amount); + result[i] = match funct6 { + NI_VNCLIPU => { + let result_wide = (vs2_val[i] >> shift_amount) + zero_extend('o, rounding_incr); + unsigned_saturation('m, result_wide) + }, + NI_VNCLIP => { + let v_double : bits('m * 4) = sign_extend(vs2_val[i]); + let result_wide = slice(v_double >> shift_amount, 0, 'o) + zero_extend('o, rounding_incr); + signed_saturation('m, result_wide) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping nitype_mnemonic : nifunct6 <-> string = { + NI_VNCLIPU <-> "vnclipu.wi", + NI_VNCLIP <-> "vnclip.wi" +} + +mapping clause assembly = NITYPE(funct6, vm, vs2, simm, vd) + <-> nitype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) ^ maybe_vmask(vm) + +/* ***************** OPIVI (Vector Slide & Gather Instructions) ****************** */ +/* Slide and gather instructions extend rs1/imm to XLEN intead of SEW bits */ +union clause ast = VISG : (visgfunct6, bits(1), regidx, bits(5), regidx) + +mapping encdec_visgfunct6 : visgfunct6 <-> bits(6) = { + VI_VSLIDEUP <-> 0b001110, + VI_VSLIDEDOWN <-> 0b001111, + VI_VRGATHER <-> 0b001100 +} + +mapping clause encdec = VISG(funct6, vm, vs2, simm, vd) if haveVExt() + <-> encdec_visgfunct6(funct6) @ vm @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VISG(funct6, vm, vs2, simm, vd)) = { + let SEW_pow = get_sew_pow(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let VLEN_pow = get_vlen_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let imm_val : nat = unsigned(zero_extend(sizeof(xlen), simm)); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VI_VSLIDEUP => { + if (vs2 == vd) then { handle_illegal(); return RETIRE_FAIL }; + if i >= imm_val then vs2_val[i - imm_val] else vd_val[i] + }, + VI_VSLIDEDOWN => { + let VLMAX = int_power(2, LMUL_pow + VLEN_pow - SEW_pow); + assert(VLMAX > 0 & VLMAX <= 'n); + if i + imm_val < VLMAX then vs2_val[i + imm_val] else zeros() + }, + VI_VRGATHER => { + if (vs2 == vd) then { handle_illegal(); return RETIRE_FAIL }; + let VLMAX = int_power(2, LMUL_pow + VLEN_pow - SEW_pow); + assert(VLMAX > 0 & VLMAX <= 'n); + if imm_val < VLMAX then vs2_val[imm_val] else zeros() + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping visg_mnemonic : visgfunct6 <-> string = { + VI_VSLIDEUP <-> "vslideup.vi", + VI_VSLIDEDOWN <-> "vslidedown.vi", + VI_VRGATHER <-> "vrgather.vi" +} + +mapping clause assembly = VISG(funct6, vm, vs2, simm, vd) + <-> visg_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(simm) ^ maybe_vmask(vm) + +/* ********************** OPIVI (Integer Merge Instruction) ********************** */ +union clause ast = MASKTYPEI : (regidx, bits(5), regidx) + +mapping clause encdec = MASKTYPEI(vs2, simm, vd) if haveVExt() + <-> 0b010111 @ 0b0 @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MASKTYPEI(vs2, simm, vd)) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); /* max(VLMAX,VLEN/SEW)) */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / (0 - LMUL_pow); /* VLMAX */ + + if illegal_vd_masked(vd) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, 0b00000); + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + + let tail_ag : agtype = get_vtype_vta(); + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + result[i] = vd_val[i] + } else if i > end_element | i >= real_num_elem then { + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + } + } else { + /* the merge operates on all body elements */ + result[i] = if vm_val[i] then imm_val else vs2_val[i] + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = MASKTYPEI(vs2, simm, vd) + <-> "vmerge.vim" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) ^ sep() ^ "v0" + +/* ********************** OPIVI (Integer Move Instruction) *********************** */ +union clause ast = MOVETYPEI : (regidx, bits(5)) + +mapping clause encdec = MOVETYPEI (vd, simm) if haveVExt() + <-> 0b010111 @ 0b1 @ 0b00000 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MOVETYPEI(vd, simm)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b1, 0b00000); + let imm_val : bits('m) = sign_extend(simm); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then result[i] = imm_val + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = MOVETYPEI(vd, simm) + <-> "vmv.v.i" ^ spc() ^ vreg_name(vd) ^ sep() ^ hex_bits_5(simm) + +/* ********************* OPIVI (Whole Vector Register Move) ********************** */ +union clause ast = VMVRTYPE : (regidx, bits(5), regidx) + +mapping clause encdec = VMVRTYPE(vs2, simm, vd) if haveVExt() + <-> 0b100111 @ 0b1 @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VMVRTYPE(vs2, simm, vd)) = { + let start_element = get_start_element(); + let SEW = get_sew(); + let imm_val = unsigned(zero_extend(sizeof(xlen), simm)); + let EMUL = imm_val + 1; + + if not(EMUL == 1 | EMUL == 2 | EMUL == 4 | EMUL == 8) then { handle_illegal(); return RETIRE_FAIL }; + + let EMUL_pow = log2(EMUL); + let num_elem = get_num_elem(EMUL_pow, SEW); + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b1, 0b00000); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, EMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, EMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + + foreach (i from 0 to (num_elem - 1)) { + result[i] = if i < start_element then vd_val[i] else vs2_val[i] + }; + + write_vreg(num_elem, SEW, EMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping simm_string : bits(5) <-> string = { + 0b00000 <-> "1", + 0b00001 <-> "2", + 0b00011 <-> "4", + 0b00111 <-> "8" +} + +mapping clause assembly = VMVRTYPE(vs2, simm, vd) + <-> "vmv" ^ simm_string(simm) ^ "r.v" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) + +/* ******************************* OPMVV (VVTYPE) ******************************** */ +union clause ast = MVVTYPE : (mvvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_mvvfunct6 : mvvfunct6 <-> bits(6) = { + MVV_VAADDU <-> 0b001000, + MVV_VAADD <-> 0b001001, + MVV_VASUBU <-> 0b001010, + MVV_VASUB <-> 0b001011, + MVV_VMUL <-> 0b100101, + MVV_VMULH <-> 0b100111, + MVV_VMULHU <-> 0b100100, + MVV_VMULHSU <-> 0b100110, + MVV_VDIVU <-> 0b100000, + MVV_VDIV <-> 0b100001, + MVV_VREMU <-> 0b100010, + MVV_VREM <-> 0b100011 +} + +mapping clause encdec = MVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_mvvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + MVV_VAADDU => { + let result_add = zero_extend('m + 1, vs2_val[i]) + zero_extend('m + 1, vs1_val[i]); + let rounding_incr = get_fixed_rounding_incr(result_add, 1); + slice(result_add >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVV_VAADD => { + let result_add = sign_extend('m + 1, vs2_val[i]) + sign_extend('m + 1, vs1_val[i]); + let rounding_incr = get_fixed_rounding_incr(result_add, 1); + slice(result_add >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVV_VASUBU => { + let result_sub = zero_extend('m + 1, vs2_val[i]) - zero_extend('m + 1, vs1_val[i]); + let rounding_incr = get_fixed_rounding_incr(result_sub, 1); + slice(result_sub >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVV_VASUB => { + let result_sub = sign_extend('m + 1, vs2_val[i]) - sign_extend('m + 1, vs1_val[i]); + let rounding_incr = get_fixed_rounding_incr(result_sub, 1); + slice(result_sub >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVV_VMUL => get_slice_int(SEW, signed(vs2_val[i]) * signed(vs1_val[i]), 0), + MVV_VMULH => get_slice_int(SEW, signed(vs2_val[i]) * signed(vs1_val[i]), SEW), + MVV_VMULHU => get_slice_int(SEW, unsigned(vs2_val[i]) * unsigned(vs1_val[i]), SEW), + MVV_VMULHSU => get_slice_int(SEW, signed(vs2_val[i]) * unsigned(vs1_val[i]), SEW), + MVV_VDIVU => { + let q : int = if unsigned(vs1_val[i]) == 0 then -1 else quot_round_zero(unsigned(vs2_val[i]), unsigned(vs1_val[i])); + to_bits(SEW, q) + }, + MVV_VDIV => { + let elem_max : int = 2 ^ (SEW - 1) - 1; + let elem_min : int = 0 - 2 ^ (SEW - 1); + let q : int = if signed(vs1_val[i]) == 0 then -1 else quot_round_zero(signed(vs2_val[i]), signed(vs1_val[i])); + /* check for signed overflow */ + let q' : int = if q > elem_max then elem_min else q; + to_bits(SEW, q') + }, + MVV_VREMU => { + let r : int = if unsigned(vs1_val[i]) == 0 then unsigned(vs2_val[i]) else rem_round_zero(unsigned(vs2_val[i]), unsigned(vs1_val[i])); + /* signed overflow case returns zero naturally as required due to -1 divisor */ + to_bits(SEW, r) + }, + MVV_VREM => { + let r : int = if signed(vs1_val[i]) == 0 then signed(vs2_val[i]) else rem_round_zero(signed(vs2_val[i]), signed(vs1_val[i])); + /* signed overflow case returns zero naturally as required due to -1 divisor */ + to_bits(SEW, r) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping mvvtype_mnemonic : mvvfunct6 <-> string = { + MVV_VAADDU <-> "vaaddu.vv", + MVV_VAADD <-> "vaadd.vv", + MVV_VASUBU <-> "vasubu.vv", + MVV_VASUB <-> "vasub.vv", + MVV_VMUL <-> "vmul.vv", + MVV_VMULH <-> "vmulh.vv", + MVV_VMULHU <-> "vmulhu.vv", + MVV_VMULHSU <-> "vmulhsu.vv", + MVV_VDIVU <-> "vdivu.vv", + MVV_VDIV <-> "vdiv.vv", + MVV_VREMU <-> "vremu.vv", + MVV_VREM <-> "vrem.vv" +} + +mapping clause assembly = MVVTYPE(funct6, vm, vs2, vs1, vd) + <-> mvvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ************************* OPMVV (VVtype Multiply-Add) ************************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = MVVMATYPE : (mvvmafunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_mvvmafunct6 : mvvmafunct6 <-> bits(6) = { + MVV_VMACC <-> 0b101101, + MVV_VNMSAC <-> 0b101111, + MVV_VMADD <-> 0b101001, + MVV_VNMSUB <-> 0b101011 +} + +mapping clause encdec = MVVMATYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_mvvmafunct6(funct6) @ vm @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MVVMATYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + MVV_VMACC => get_slice_int(SEW, signed(vs1_val[i]) * signed(vs2_val[i]), 0) + vd_val[i], + MVV_VNMSAC => vd_val[i] - get_slice_int(SEW, signed(vs1_val[i]) * signed(vs2_val[i]), 0), + MVV_VMADD => get_slice_int(SEW, signed(vs1_val[i]) * signed(vd_val[i]), 0) + vs2_val[i], + MVV_VNMSUB => vs2_val[i] - get_slice_int(SEW, signed(vs1_val[i]) * signed(vd_val[i]), 0) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping mvvmatype_mnemonic : mvvmafunct6 <-> string = { + MVV_VMACC <-> "vmacc.vv", + MVV_VNMSAC <-> "vnmsac.vv", + MVV_VMADD <-> "vmadd.vv", + MVV_VNMSUB <-> "vnmsub.vv" +} + +mapping clause assembly = MVVMATYPE(funct6, vm, vs2, vs1, vd) + <-> mvvmatype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* *************************** OPMVV (VVTYPE Widening) *************************** */ +union clause ast = WVVTYPE : (wvvfunct6, bits(1), regidx, regidx, regidx) +mapping encdec_wvvfunct6 : wvvfunct6 <-> bits(6) = { + WVV_VADD <-> 0b110001, + WVV_VSUB <-> 0b110011, + WVV_VADDU <-> 0b110000, + WVV_VSUBU <-> 0b110010, + WVV_VWMUL <-> 0b111011, + WVV_VWMULU <-> 0b111000, + WVV_VWMULSU <-> 0b111010 +} + +mapping clause encdec = WVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_wvvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(WVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs1, vd, LMUL_pow, LMUL_pow_widen)) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + WVV_VADD => to_bits(SEW_widen, signed(vs2_val[i]) + signed(vs1_val[i])), + WVV_VSUB => to_bits(SEW_widen, signed(vs2_val[i]) - signed(vs1_val[i])), + WVV_VADDU => to_bits(SEW_widen, unsigned(vs2_val[i]) + unsigned(vs1_val[i])), + WVV_VSUBU => to_bits(SEW_widen, unsigned(vs2_val[i]) - unsigned(vs1_val[i])), + WVV_VWMUL => to_bits(SEW_widen, signed(vs2_val[i]) * signed(vs1_val[i])), + WVV_VWMULU => to_bits(SEW_widen, unsigned(vs2_val[i]) * unsigned(vs1_val[i])), + WVV_VWMULSU => to_bits(SEW_widen, signed(vs2_val[i]) * unsigned(vs1_val[i])) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping wvvtype_mnemonic : wvvfunct6 <-> string = { + WVV_VADD <-> "vwadd.vv", + WVV_VSUB <-> "vwsub.vv", + WVV_VADDU <-> "vwaddu.vv", + WVV_VSUBU <-> "vwsubu.vv", + WVV_VWMUL <-> "vwmul.vv", + WVV_VWMULU <-> "vwmulu.vv", + WVV_VWMULSU <-> "vwmulsu.vv" +} + +mapping clause assembly = WVVTYPE(funct6, vm, vs2, vs1, vd) + <-> wvvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* *************************** OPMVV (WVTYPE Widening) *************************** */ +union clause ast = WVTYPE : (wvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_wvfunct6 : wvfunct6 <-> bits(6) = { + WV_VADD <-> 0b110101, + WV_VSUB <-> 0b110111, + WV_VADDU <-> 0b110100, + WV_VSUBU <-> 0b110110 +} + +mapping clause encdec = WVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_wvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(WVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs1, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + WV_VADD => to_bits(SEW_widen, signed(vs2_val[i]) + signed(vs1_val[i])), + WV_VSUB => to_bits(SEW_widen, signed(vs2_val[i]) - signed(vs1_val[i])), + WV_VADDU => to_bits(SEW_widen, unsigned(vs2_val[i]) + unsigned(vs1_val[i])), + WV_VSUBU => to_bits(SEW_widen, unsigned(vs2_val[i]) - unsigned(vs1_val[i])) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping wvtype_mnemonic : wvfunct6 <-> string = { + WV_VADD <-> "vwadd.wv", + WV_VSUB <-> "vwsub.wv", + WV_VADDU <-> "vwaddu.wv", + WV_VSUBU <-> "vwsubu.wv" +} + +mapping clause assembly = WVTYPE(funct6, vm, vs2, vs1, vd) + <-> wvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ******************** OPMVV (VVtype Widening Multiply-Add) ********************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = WMVVTYPE : (wmvvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_wmvvfunct6 : wmvvfunct6 <-> bits(6) = { + WMVV_VWMACCU <-> 0b111100, + WMVV_VWMACC <-> 0b111101, + WMVV_VWMACCSU <-> 0b111111 +} + +mapping clause encdec = WMVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_wmvvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(WMVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs1, vd, LMUL_pow, LMUL_pow_widen)) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + WMVV_VWMACC => to_bits(SEW_widen, signed(vs1_val[i]) * signed(vs2_val[i])) + vd_val[i], + WMVV_VWMACCU => to_bits(SEW_widen, unsigned(vs1_val[i]) * unsigned(vs2_val[i])) + vd_val[i], + WMVV_VWMACCSU => to_bits(SEW_widen, signed(vs1_val[i]) * unsigned(vs2_val[i]))+ vd_val[i] + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping wmvvtype_mnemonic : wmvvfunct6 <-> string = { + WMVV_VWMACCU <-> "vwmaccu.vv", + WMVV_VWMACC <-> "vwmacc.vv", + WMVV_VWMACCSU <-> "vwmaccsu.vv" +} + +mapping clause assembly = WMVVTYPE(funct6, vm, vs2, vs1, vd) + <-> wmvvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ****************************** OPMVV (VXUNARY0) ******************************* */ +/* ******************* Vector Integer Extension (SEW/2 source) ******************* */ +union clause ast = VEXT2TYPE : (vext2funct6, bits(1), regidx, regidx) + +mapping vext2_vs1 : vext2funct6 <-> bits(5) = { + VEXT2_ZVF2 <-> 0b00110, + VEXT2_SVF2 <-> 0b00111 +} + +mapping clause encdec = VEXT2TYPE(funct6, vm, vs2, vd) if haveVExt() + <-> 0b010010 @ vm @ vs2 @ vext2_vs1(funct6) @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VEXT2TYPE(funct6, vm, vs2, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_half = SEW / 2; + let LMUL_pow_half = LMUL_pow - 1; + + if illegal_variable_width(vd, vm, SEW_half, LMUL_pow_half) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_half, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_half; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_half, LMUL_pow_half, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW > SEW_half); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VEXT2_ZVF2 => zero_extend(vs2_val[i]), + VEXT2_SVF2 => sign_extend(vs2_val[i]) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vext2type_mnemonic : vext2funct6 <-> string = { + VEXT2_ZVF2 <-> "vzext.vf2", + VEXT2_SVF2 <-> "vsext.vf2" +} + +mapping clause assembly = VEXT2TYPE(funct6, vm, vs2, vd) + <-> vext2type_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ****************************** OPMVV (VXUNARY0) ******************************* */ +/* ******************* Vector Integer Extension (SEW/4 source) ******************* */ +union clause ast = VEXT4TYPE : (vext4funct6, bits(1), regidx, regidx) + +mapping vext4_vs1 : vext4funct6 <-> bits(5) = { + VEXT4_ZVF4 <-> 0b00100, + VEXT4_SVF4 <-> 0b00101 +} + +mapping clause encdec = VEXT4TYPE(funct6, vm, vs2, vd) if haveVExt() + <-> 0b010010 @ vm @ vs2 @ vext4_vs1(funct6) @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VEXT4TYPE(funct6, vm, vs2, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_quart = SEW / 4; + let LMUL_pow_quart = LMUL_pow - 2; + + if illegal_variable_width(vd, vm, SEW_quart, LMUL_pow_quart) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_quart, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_quart; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_quart, LMUL_pow_quart, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW > SEW_quart); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VEXT4_ZVF4 => zero_extend(vs2_val[i]), + VEXT4_SVF4 => sign_extend(vs2_val[i]) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vext4type_mnemonic : vext4funct6 <-> string = { + VEXT4_ZVF4 <-> "vzext.vf4", + VEXT4_SVF4 <-> "vsext.vf4" +} + +mapping clause assembly = VEXT4TYPE(funct6, vm, vs2, vd) + <-> vext4type_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ****************************** OPMVV (VXUNARY0) ******************************* */ +/* ******************* Vector Integer Extension (SEW/8 source) ******************* */ +union clause ast = VEXT8TYPE : (vext8funct6, bits(1), regidx, regidx) + +mapping vext8_vs1 : vext8funct6 <-> bits(5) = { + VEXT8_ZVF8 <-> 0b00010, + VEXT8_SVF8 <-> 0b00011 +} + +mapping clause encdec = VEXT8TYPE(funct6, vm, vs2, vd) if haveVExt() + <-> 0b010010 @ vm @ vs2 @ vext8_vs1(funct6) @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VEXT8TYPE(funct6, vm, vs2, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_eighth = SEW / 8; + let LMUL_pow_eighth = LMUL_pow - 3; + + if illegal_variable_width(vd, vm, SEW_eighth, LMUL_pow_eighth) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_eighth, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_eighth; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_eighth, LMUL_pow_eighth, vs2); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + assert(SEW > SEW_eighth); + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VEXT8_ZVF8 => zero_extend(vs2_val[i]), + VEXT8_SVF8 => sign_extend(vs2_val[i]) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vext8type_mnemonic : vext8funct6 <-> string = { + VEXT8_ZVF8 <-> "vzext.vf8", + VEXT8_SVF8 <-> "vsext.vf8" +} + +mapping clause assembly = VEXT8TYPE(funct6, vm, vs2, vd) + <-> vext8type_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************ OPMVV (vmv.x.s in VWXUNARY0) ************************* */ +union clause ast = VMVXS : (regidx, regidx) + +mapping clause encdec = VMVXS(vs2, rd) if haveVExt() + <-> 0b010000 @ 0b1 @ vs2 @ 0b00000 @ 0b010 @ rd @ 0b1010111 if haveVExt() + +function clause execute(VMVXS(vs2, rd)) = { + let SEW = get_sew(); + let num_elem = get_num_elem(0, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + assert(num_elem > 0); + let 'n = num_elem; + let 'm = SEW; + + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, 0, vs2); + X(rd) = if sizeof(xlen) < SEW then slice(vs2_val[0], 0, sizeof(xlen)) + else if sizeof(xlen) > SEW then sign_extend(vs2_val[0]) + else vs2_val[0]; + vstart = zeros(); + + RETIRE_SUCCESS +} + +mapping clause assembly = VMVXS(vs2, rd) + <-> "vmv.x.s" ^ spc() ^ reg_name(rd) ^ sep() ^ vreg_name(vs2) + +/* ********************* OPMVV (Vector Compress Instruction) ********************* */ +union clause ast = MVVCOMPRESS : (regidx, regidx, regidx) + +mapping clause encdec = MVVCOMPRESS(vs2, vs1, vd) if haveVExt() + <-> 0b010111 @ 0b1 @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MVVCOMPRESS(vs2, vs1, vd)) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + /* vcompress should always be executed with a vstart of 0 */ + if start_element != 0 | vs1 == vd | vs2 == vd | illegal_vd_unmasked() + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vs1_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + + /* body elements */ + vd_idx : nat = 0; + foreach (i from 0 to (num_elem - 1)) { + if i <= end_element then { + if vs1_val[i] then { + let 'p = vd_idx; + assert('p < 'n); + result['p] = vs2_val[i]; + vd_idx = vd_idx + 1; + } + } + }; + /* tail elements */ + if vd_idx < num_elem then { + let tail_ag : agtype = get_vtype_vta(); + let 'p = vd_idx; + foreach (i from 'p to (num_elem - 1)) { + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = MVVCOMPRESS(vs2, vs1, vd) + <-> "vcompress.vm" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) + +/* ******************************* OPMVX (VXTYPE) ******************************** */ +union clause ast = MVXTYPE : (mvxfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_mvxfunct6 : mvxfunct6 <-> bits(6) = { + MVX_VAADDU <-> 0b001000, + MVX_VAADD <-> 0b001001, + MVX_VASUBU <-> 0b001010, + MVX_VASUB <-> 0b001011, + MVX_VSLIDE1UP <-> 0b001110, + MVX_VSLIDE1DOWN <-> 0b001111, + MVX_VMUL <-> 0b100101, + MVX_VMULH <-> 0b100111, + MVX_VMULHU <-> 0b100100, + MVX_VMULHSU <-> 0b100110, + MVX_VDIVU <-> 0b100000, + MVX_VDIV <-> 0b100001, + MVX_VREMU <-> 0b100010, + MVX_VREM <-> 0b100011 +} + +mapping clause encdec = MVXTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_mvxfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b110 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MVXTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + MVX_VAADDU => { + let result_add = zero_extend('m + 1, vs2_val[i]) + zero_extend('m + 1, rs1_val); + let rounding_incr = get_fixed_rounding_incr(result_add, 1); + slice(result_add >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVX_VAADD => { + let result_add = sign_extend('m + 1, vs2_val[i]) + sign_extend('m + 1, rs1_val); + let rounding_incr = get_fixed_rounding_incr(result_add, 1); + slice(result_add >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVX_VASUBU => { + let result_sub = zero_extend('m + 1, vs2_val[i]) - zero_extend('m + 1, rs1_val); + let rounding_incr = get_fixed_rounding_incr(result_sub, 1); + slice(result_sub >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVX_VASUB => { + let result_sub = sign_extend('m + 1, vs2_val[i]) - sign_extend('m + 1, rs1_val); + let rounding_incr = get_fixed_rounding_incr(result_sub, 1); + slice(result_sub >> 1, 0, 'm) + zero_extend('m, rounding_incr) + }, + MVX_VSLIDE1UP => { + if (vs2 == vd) then { handle_illegal(); return RETIRE_FAIL }; + if i == 0 then rs1_val else vs2_val[i - 1] + }, + MVX_VSLIDE1DOWN => { + let last_elem = get_end_element(); + assert(last_elem < num_elem); + if i < last_elem then vs2_val[i + 1] else rs1_val + }, + MVX_VMUL => get_slice_int(SEW, signed(vs2_val[i]) * signed(rs1_val), 0), + MVX_VMULH => get_slice_int(SEW, signed(vs2_val[i]) * signed(rs1_val), SEW), + MVX_VMULHU => get_slice_int(SEW, unsigned(vs2_val[i]) * unsigned(rs1_val), SEW), + MVX_VMULHSU => get_slice_int(SEW, signed(vs2_val[i]) * unsigned(rs1_val), SEW), + MVX_VDIVU => { + let q : int = if unsigned(rs1_val) == 0 then -1 else quot_round_zero(unsigned(vs2_val[i]), unsigned(rs1_val)); + to_bits(SEW, q) + }, + MVX_VDIV => { + let elem_max : int = 2 ^ (SEW - 1) - 1; + let elem_min : int = 0 - 2 ^ (SEW - 1); + let q : int = if signed(rs1_val) == 0 then -1 else quot_round_zero(signed(vs2_val[i]), signed(rs1_val)); + /* check for signed overflow */ + let q' : int = if q > elem_max then elem_min else q; + to_bits(SEW, q') + }, + MVX_VREMU => { + let r : int = if unsigned(rs1_val) == 0 then unsigned(vs2_val[i]) else rem_round_zero(unsigned(vs2_val[i]), unsigned (rs1_val)); + /* signed overflow case returns zero naturally as required due to -1 divisor */ + to_bits(SEW, r) + }, + MVX_VREM => { + let r : int = if signed(rs1_val) == 0 then signed(vs2_val[i]) else rem_round_zero(signed(vs2_val[i]), signed(rs1_val)); + /* signed overflow case returns zero naturally as required due to -1 divisor */ + to_bits(SEW, r) + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping mvxtype_mnemonic : mvxfunct6 <-> string = { + MVX_VAADDU <-> "vaaddu.vx", + MVX_VAADD <-> "vaadd.vx", + MVX_VASUBU <-> "vasubu.vx", + MVX_VASUB <-> "vasub.vx", + MVX_VSLIDE1UP <-> "vslide1up.vx", + MVX_VSLIDE1DOWN <-> "vslide1down.vx", + MVX_VMUL <-> "vmul.vx", + MVX_VMULH <-> "vmulh.vx", + MVX_VMULHU <-> "vmulhu.vx", + MVX_VMULHSU <-> "vmulhsu.vx", + MVX_VDIVU <-> "vdivu.vx", + MVX_VDIV <-> "vdiv.vx", + MVX_VREMU <-> "vremu.vx", + MVX_VREM <-> "vrem.vx" +} + +mapping clause assembly = MVXTYPE(funct6, vm, vs2, rs1, vd) + <-> mvxtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ************************* OPMVX (VXtype Multiply-Add) ************************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = MVXMATYPE : (mvxmafunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_mvxmafunct6 : mvxmafunct6 <-> bits(6) = { + MVX_VMACC <-> 0b101101, + MVX_VNMSAC <-> 0b101111, + MVX_VMADD <-> 0b101001, + MVX_VNMSUB <-> 0b101011 +} + +mapping clause encdec = MVXMATYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_mvxmafunct6(funct6) @ vm @ vs2 @ rs1 @ 0b110 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MVXMATYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + MVX_VMACC => get_slice_int(SEW, signed(rs1_val) * signed(vs2_val[i]), 0) + vd_val[i], + MVX_VNMSAC => vd_val[i] - get_slice_int(SEW, signed(rs1_val) * signed(vs2_val[i]), 0), + MVX_VMADD => get_slice_int(SEW, signed(rs1_val) * signed(vd_val[i]), 0) + vs2_val[i], + MVX_VNMSUB => vs2_val[i] - get_slice_int(SEW, signed(rs1_val) * signed(vd_val[i]), 0) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping mvxmatype_mnemonic : mvxmafunct6 <-> string = { + MVX_VMACC <-> "vmacc.vx", + MVX_VNMSAC <-> "vnmsac.vx", + MVX_VMADD <-> "vmadd.vx", + MVX_VNMSUB <-> "vnmsub.vx" +} + +mapping clause assembly = MVXMATYPE(funct6, vm, vs2, rs1, vd) + <-> mvxmatype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ reg_name(rs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* *************************** OPMVX (VXTYPE Widening) *************************** */ +union clause ast = WVXTYPE : (wvxfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_wvxfunct6 : wvxfunct6 <-> bits(6) = { + WVX_VADD <-> 0b110001, + WVX_VSUB <-> 0b110011, + WVX_VADDU <-> 0b110000, + WVX_VSUBU <-> 0b110010, + WVX_VWMUL <-> 0b111011, + WVX_VWMULU <-> 0b111000, + WVX_VWMULSU <-> 0b111010 +} + +mapping clause encdec = WVXTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_wvxfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b110 @ vd @ 0b1010111 if haveVExt() + +function clause execute(WVXTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + WVX_VADD => to_bits(SEW_widen, signed(vs2_val[i]) + signed(rs1_val)), + WVX_VSUB => to_bits(SEW_widen, signed(vs2_val[i]) - signed(rs1_val)), + WVX_VADDU => to_bits(SEW_widen, unsigned(vs2_val[i]) + unsigned(rs1_val)), + WVX_VSUBU => to_bits(SEW_widen, unsigned(vs2_val[i]) - unsigned(rs1_val)), + WVX_VWMUL => to_bits(SEW_widen, signed(vs2_val[i]) * signed(rs1_val)), + WVX_VWMULU => to_bits(SEW_widen, unsigned(vs2_val[i]) * unsigned(rs1_val)), + WVX_VWMULSU => to_bits(SEW_widen, signed(vs2_val[i]) * unsigned(rs1_val)) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping wvxtype_mnemonic : wvxfunct6 <-> string = { + WVX_VADD <-> "vwadd.vx", + WVX_VSUB <-> "vwsub.vx", + WVX_VADDU <-> "vwaddu.vx", + WVX_VSUBU <-> "vwsubu.vx", + WVX_VWMUL <-> "vwmul.vx", + WVX_VWMULU <-> "vwmulu.vx", + WVX_VWMULSU <-> "vwmulsu.vx" +} + +mapping clause assembly = WVXTYPE(funct6, vm, vs2, rs1, vd) + <-> wvxtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* *************************** OPMVX (WXTYPE Widening) *************************** */ +union clause ast = WXTYPE : (wxfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_wxfunct6 : wxfunct6 <-> bits(6) = { + WX_VADD <-> 0b110101, + WX_VSUB <-> 0b110111, + WX_VADDU <-> 0b110100, + WX_VSUBU <-> 0b110110 +} + +mapping clause encdec = WXTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_wxfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b110 @ vd @ 0b1010111 if haveVExt() + +function clause execute(WXTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + WX_VADD => to_bits(SEW_widen, signed(vs2_val[i]) + signed(rs1_val)), + WX_VSUB => to_bits(SEW_widen, signed(vs2_val[i]) - signed(rs1_val)), + WX_VADDU => to_bits(SEW_widen, unsigned(vs2_val[i]) + unsigned(rs1_val)), + WX_VSUBU => to_bits(SEW_widen, unsigned(vs2_val[i]) - unsigned(rs1_val)) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping wxtype_mnemonic : wxfunct6 <-> string = { + WX_VADD <-> "vwadd.wx", + WX_VSUB <-> "vwsub.wx", + WX_VADDU <-> "vwaddu.wx", + WX_VSUBU <-> "vwsubu.wx" +} + +mapping clause assembly = WXTYPE(funct6, vm, vs2, rs1, vd) + <-> wxtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ******************** OPMVX (VXtype Widening Multiply-Add) ********************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = WMVXTYPE : (wmvxfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_wmvxfunct6 : wmvxfunct6 <-> bits(6) = { + WMVX_VWMACCU <-> 0b111100, + WMVX_VWMACC <-> 0b111101, + WMVX_VWMACCUS <-> 0b111110, + WMVX_VWMACCSU <-> 0b111111 +} + +mapping clause encdec = WMVXTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_wmvxfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b110 @ vd @ 0b1010111 if haveVExt() + +function clause execute(WMVXTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_variable_width(vd, vm, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + WMVX_VWMACCU => (to_bits(SEW_widen, unsigned(rs1_val) * unsigned(vs2_val[i]) )) + vd_val[i], + WMVX_VWMACC => (to_bits(SEW_widen, signed(rs1_val) * signed(vs2_val[i]) )) + vd_val[i], + WMVX_VWMACCUS => (to_bits(SEW_widen, unsigned(rs1_val) * signed(vs2_val[i]) ))+ vd_val[i], + WMVX_VWMACCSU => (to_bits(SEW_widen, signed(rs1_val) * unsigned(vs2_val[i]) ))+ vd_val[i] + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping wmvxtype_mnemonic : wmvxfunct6 <-> string = { + WMVX_VWMACCU <-> "vwmaccu.vx", + WMVX_VWMACC <-> "vwmacc.vx", + WMVX_VWMACCUS <-> "vwmaccus.vx", + WMVX_VWMACCSU <-> "vwmaccsu.vx" +} + +mapping clause assembly = WMVXTYPE(funct6, vm, vs2, rs1, vd) + <-> wmvxtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ reg_name(rs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ****************************** OPMVX (VRXUNARY0) ****************************** */ +union clause ast = VMVSX : (regidx, regidx) + +mapping clause encdec = VMVSX(rs1, vd) if haveVExt() + <-> 0b010000 @ 0b1 @ 0b00000 @ rs1 @ 0b110 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VMVSX(rs1, vd)) = { + let SEW = get_sew(); + let num_elem = get_num_elem(0, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + assert(num_elem > 0); + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b1, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, 'm); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, 0, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, 0, vd_val, vm_val); + + /* one body element */ + if mask[0] then result[0] = rs1_val; + + /* others treated as tail elements */ + let tail_ag : agtype = get_vtype_vta(); + foreach (i from 1 to (num_elem - 1)) { + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + } + }; + + write_vreg(num_elem, SEW, 0, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VMVSX(rs1, vd) + <-> "vmv.s.x" ^ spc() ^ vreg_name(vd) ^ sep() ^ reg_name(rs1) diff --git a/model/riscv_insts_vext_fp.sail b/model/riscv_insts_vext_fp.sail new file mode 100755 index 000000000..bba7e5448 --- /dev/null +++ b/model/riscv_insts_vext_fp.sail @@ -0,0 +1,1363 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements part of the vector extension. */ +/* Chapter 13: Vector Floating-Point Instructions */ +/* Chapter 16: Vector Permutation Instructions (floating-point part) */ +/* ******************************************************************************* */ + +/* ******************************* OPFVV (VVTYPE) ******************************** */ +union clause ast = FVVTYPE : (fvvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fvvfunct6 : fvvfunct6 <-> bits(6) = { + FVV_VADD <-> 0b000000, + FVV_VSUB <-> 0b000010, + FVV_VMIN <-> 0b000100, + FVV_VMAX <-> 0b000110, + FVV_VSGNJ <-> 0b001000, + FVV_VSGNJN <-> 0b001001, + FVV_VSGNJX <-> 0b001010, + FVV_VDIV <-> 0b100000, + FVV_VMUL <-> 0b100100 +} + +mapping clause encdec = FVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_fvvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_normal(vd, vm, SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FVV_VADD => fp_add(rm_3b, vs2_val[i], vs1_val[i]), + FVV_VSUB => fp_sub(rm_3b, vs2_val[i], vs1_val[i]), + FVV_VMIN => fp_min(vs2_val[i], vs1_val[i]), + FVV_VMAX => fp_max(vs2_val[i], vs1_val[i]), + FVV_VMUL => fp_mul(rm_3b, vs2_val[i], vs1_val[i]), + FVV_VDIV => fp_div(rm_3b, vs2_val[i], vs1_val[i]), + FVV_VSGNJ => [vs1_val[i]['m - 1]] @ vs2_val[i][('m - 2)..0], + FVV_VSGNJN => (0b1 ^ [vs1_val[i]['m - 1]]) @ vs2_val[i][('m - 2)..0], + FVV_VSGNJX => ([vs2_val[i]['m - 1]] ^ [vs1_val[i]['m - 1]]) @ vs2_val[i][('m - 2)..0] + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fvvtype_mnemonic : fvvfunct6 <-> string = { + FVV_VADD <-> "vfadd.vv", + FVV_VSUB <-> "vfsub.vv", + FVV_VMIN <-> "vfmin.vv", + FVV_VMAX <-> "vfmax.vv", + FVV_VSGNJ <-> "vfsgnj.vv", + FVV_VSGNJN <-> "vfsgnjn.vv", + FVV_VSGNJX <-> "vfsgnjx.vv", + FVV_VDIV <-> "vfdiv.vv", + FVV_VMUL <-> "vfmul.vv" +} + +mapping clause assembly = FVVTYPE(funct6, vm, vs2, vs1, vd) + <-> fvvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ************************* OPFVV (VVtype Multiply-Add) ************************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = FVVMATYPE : (fvvmafunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fvvmafunct6 : fvvmafunct6 <-> bits(6) = { + FVV_VMADD <-> 0b101000, + FVV_VNMADD <-> 0b101001, + FVV_VMSUB <-> 0b101010, + FVV_VNMSUB <-> 0b101011, + FVV_VMACC <-> 0b101100, + FVV_VNMACC <-> 0b101101, + FVV_VMSAC <-> 0b101110, + FVV_VNMSAC <-> 0b101111 +} + +mapping clause encdec = FVVMATYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_fvvmafunct6(funct6) @ vm @ vs2 @ vs1 @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FVVMATYPE(funct6, vm, vs2, vs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_normal(vd, vm, SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FVV_VMACC => fp_muladd(rm_3b, vs1_val[i], vs2_val[i], vd_val[i]), + FVV_VNMACC => fp_nmulsub(rm_3b, vs1_val[i], vs2_val[i], vd_val[i]), + FVV_VMSAC => fp_mulsub(rm_3b, vs1_val[i], vs2_val[i], vd_val[i]), + FVV_VNMSAC => fp_nmuladd(rm_3b, vs1_val[i], vs2_val[i], vd_val[i]), + FVV_VMADD => fp_muladd(rm_3b, vs1_val[i], vd_val[i], vs2_val[i]), + FVV_VNMADD => fp_nmulsub(rm_3b, vs1_val[i], vd_val[i], vs2_val[i]), + FVV_VMSUB => fp_mulsub(rm_3b, vs1_val[i], vd_val[i], vs2_val[i]), + FVV_VNMSUB => fp_nmuladd(rm_3b, vs1_val[i], vd_val[i], vs2_val[i]) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fvvmatype_mnemonic : fvvmafunct6 <-> string = { + FVV_VMADD <-> "vfmadd.vv", + FVV_VNMADD <-> "vfnmadd.vv", + FVV_VMSUB <-> "vfmsub.vv", + FVV_VNMSUB <-> "vfnmsub.vv", + FVV_VMACC <-> "vfmacc.vv", + FVV_VNMACC <-> "vfnmacc.vv", + FVV_VMSAC <-> "vfmsac.vv", + FVV_VNMSAC <-> "vfnmsac.vv" +} + +mapping clause assembly = FVVMATYPE(funct6, vm, vs2, vs1, vd) + <-> fvvmatype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* *************************** OPFVV (VVTYPE Widening) *************************** */ +union clause ast = FWVVTYPE : (fwvvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fwvvfunct6 : fwvvfunct6 <-> bits(6) = { + FWVV_VADD <-> 0b110000, + FWVV_VSUB <-> 0b110010, + FWVV_VMUL <-> 0b111000 +} + +mapping clause encdec = FWVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_fwvvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FWVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs1, vd, LMUL_pow, LMUL_pow_widen)) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 16 & SEW_widen <= 64); + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FWVV_VADD => fp_add(rm_3b, fp_widen(vs2_val[i]), fp_widen(vs1_val[i])), + FWVV_VSUB => fp_sub(rm_3b, fp_widen(vs2_val[i]), fp_widen(vs1_val[i])), + FWVV_VMUL => fp_mul(rm_3b, fp_widen(vs2_val[i]), fp_widen(vs1_val[i])) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fwvvtype_mnemonic : fwvvfunct6 <-> string = { + FWVV_VADD <-> "vfwadd.vv", + FWVV_VSUB <-> "vfwsub.vv", + FWVV_VMUL <-> "vfwmul.vv" +} + +mapping clause assembly = FWVVTYPE(funct6, vm, vs2, vs1, vd) + <-> fwvvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ******************** OPFVV (VVtype Widening Multiply-Add) ********************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = FWVVMATYPE : (fwvvmafunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fwvvmafunct6 : fwvvmafunct6 <-> bits(6) = { + FWVV_VMACC <-> 0b111100, + FWVV_VNMACC <-> 0b111101, + FWVV_VMSAC <-> 0b111110, + FWVV_VNMSAC <-> 0b111111 +} + +mapping clause encdec = FWVVMATYPE(funct6, vm, vs1, vs2, vd) if haveVExt() + <-> encdec_fwvvmafunct6(funct6) @ vm @ vs1 @ vs2 @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FWVVMATYPE(funct6, vm, vs1, vs2, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs1, vd, LMUL_pow, LMUL_pow_widen)) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 16 & SEW_widen <= 64); + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FWVV_VMACC => fp_muladd(rm_3b, fp_widen(vs1_val[i]), fp_widen(vs2_val[i]), vd_val[i]), + FWVV_VNMACC => fp_nmulsub(rm_3b, fp_widen(vs1_val[i]), fp_widen(vs2_val[i]), vd_val[i]), + FWVV_VMSAC => fp_mulsub(rm_3b, fp_widen(vs1_val[i]), fp_widen(vs2_val[i]), vd_val[i]), + FWVV_VNMSAC => fp_nmuladd(rm_3b, fp_widen(vs1_val[i]), fp_widen(vs2_val[i]), vd_val[i]) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fwvvmatype_mnemonic : fwvvmafunct6 <-> string = { + FWVV_VMACC <-> "vfwmacc.vv", + FWVV_VNMACC <-> "vfwnmacc.vv", + FWVV_VMSAC <-> "vfwmsac.vv", + FWVV_VNMSAC <-> "vfwnmsac.vv" +} + +mapping clause assembly = FWVVMATYPE(funct6, vm, vs1, vs2, vd) + <-> fwvvmatype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* *************************** OPFVV (WVTYPE Widening) *************************** */ +union clause ast = FWVTYPE : (fwvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fwvfunct6 : fwvfunct6 <-> bits(6) = { + FWV_VADD <-> 0b110100, + FWV_VSUB <-> 0b110110 +} + +mapping clause encdec = FWVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_fwvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FWVTYPE(funct6, vm, vs2, vs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs1, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 16 & SEW_widen <= 64); + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FWV_VADD => fp_add(rm_3b, vs2_val[i], fp_widen(vs1_val[i])), + FWV_VSUB => fp_sub(rm_3b, vs2_val[i], fp_widen(vs1_val[i])) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fwvtype_mnemonic : fwvfunct6 <-> string = { + FWV_VADD <-> "vfwadd.wv", + FWV_VSUB <-> "vfwsub.wv" +} + +mapping clause assembly = FWVTYPE(funct6, vm, vs2, vs1, vd) + <-> fwvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ****************************** OPFVV (VFUNARY0) ******************************* */ +union clause ast = VFUNARY0 : (bits(1), regidx, vfunary0, regidx) + +mapping encdec_vfunary0_vs1 : vfunary0 <-> bits(5) = { + FV_CVT_XU_F <-> 0b00000, + FV_CVT_X_F <-> 0b00001, + FV_CVT_F_XU <-> 0b00010, + FV_CVT_F_X <-> 0b00011, + FV_CVT_RTZ_XU_F <-> 0b00110, + FV_CVT_RTZ_X_F <-> 0b00111 +} + +mapping clause encdec = VFUNARY0(vm, vs2, vfunary0, vd) if haveVExt() + <-> 0b010010 @ vm @ vs2 @ encdec_vfunary0_vs1(vfunary0) @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VFUNARY0(vm, vs2, vfunary0, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_normal(vd, vm, SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match vfunary0 { + FV_CVT_XU_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16ToUi16(rm_3b, vs2_val[i]), + 32 => riscv_f32ToUi32(rm_3b, vs2_val[i]), + 64 => riscv_f64ToUi64(rm_3b, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FV_CVT_X_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16ToI16(rm_3b, vs2_val[i]), + 32 => riscv_f32ToI32(rm_3b, vs2_val[i]), + 64 => riscv_f64ToI64(rm_3b, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FV_CVT_F_XU => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_ui32ToF16(rm_3b, zero_extend(vs2_val[i])), + 32 => riscv_ui32ToF32(rm_3b, vs2_val[i]), + 64 => riscv_ui64ToF64(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FV_CVT_F_X => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_i32ToF16(rm_3b, sign_extend(vs2_val[i])), + 32 => riscv_i32ToF32(rm_3b, vs2_val[i]), + 64 => riscv_i64ToF64(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FV_CVT_RTZ_XU_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16ToUi16(0b001, vs2_val[i]), + 32 => riscv_f32ToUi32(0b001, vs2_val[i]), + 64 => riscv_f64ToUi64(0b001, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FV_CVT_RTZ_X_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16ToI16(0b001, vs2_val[i]), + 32 => riscv_f32ToI32(0b001, vs2_val[i]), + 64 => riscv_f64ToI64(0b001, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vfunary0_mnemonic : vfunary0 <-> string = { + FV_CVT_XU_F <-> "vfcvt.xu.f.v", + FV_CVT_X_F <-> "vfcvt.x.f.v", + FV_CVT_F_XU <-> "vfcvt.f.xu.v", + FV_CVT_F_X <-> "vfcvt.f.x.v", + FV_CVT_RTZ_XU_F <-> "vfcvt.rtz.xu.f.v", + FV_CVT_RTZ_X_F <-> "vfcvt.rtz.x.f.v" +} + +mapping clause assembly = VFUNARY0(vm, vs2, vfunary0, vd) + <-> vfunary0_mnemonic(vfunary0) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************** OPFVV (VFUNARY0 Widening) ************************** */ +union clause ast = VFWUNARY0 : (bits(1), regidx, vfwunary0, regidx) + +mapping encdec_vfwunary0_vs1 : vfwunary0 <-> bits(5) = { + FWV_CVT_XU_F <-> 0b01000, + FWV_CVT_X_F <-> 0b01001, + FWV_CVT_F_XU <-> 0b01010, + FWV_CVT_F_X <-> 0b01011, + FWV_CVT_F_F <-> 0b01100, + FWV_CVT_RTZ_XU_F <-> 0b01110, + FWV_CVT_RTZ_X_F <-> 0b01111 +} + +mapping clause encdec = VFWUNARY0(vm, vs2, vfwunary0, vd) if haveVExt() + <-> 0b010010 @ vm @ vs2 @ encdec_vfwunary0_vs1(vfwunary0) @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VFWUNARY0(vm, vs2, vfwunary0, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 8 & SEW_widen <= 64); + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match vfwunary0 { + FWV_CVT_XU_F => { + let (fflags, elem) : (bits_fflags, bits('o)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_f16ToUi32(rm_3b, vs2_val[i]), + 32 => riscv_f32ToUi64(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FWV_CVT_X_F => { + let (fflags, elem) : (bits_fflags, bits('o)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_f16ToI32(rm_3b, vs2_val[i]), + 32 => riscv_f32ToI64(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FWV_CVT_F_XU => { + let (fflags, elem) : (bits_fflags, bits('o)) = match 'm { + 8 => riscv_ui32ToF16(rm_3b, zero_extend(vs2_val[i])), + 16 => riscv_ui32ToF32(rm_3b, zero_extend(vs2_val[i])), + 32 => riscv_ui32ToF64(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FWV_CVT_F_X => { + let (fflags, elem) : (bits_fflags, bits('o)) = match 'm { + 8 => riscv_i32ToF16(rm_3b, sign_extend(vs2_val[i])), + 16 => riscv_i32ToF32(rm_3b, sign_extend(vs2_val[i])), + 32 => riscv_i32ToF64(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FWV_CVT_F_F => { + let (fflags, elem) : (bits_fflags, bits('o)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_f16ToF32(rm_3b, vs2_val[i]), + 32 => riscv_f32ToF64(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FWV_CVT_RTZ_XU_F => { + let (fflags, elem) : (bits_fflags, bits('o)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_f16ToUi32(0b001, vs2_val[i]), + 32 => riscv_f32ToUi64(0b001, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FWV_CVT_RTZ_X_F => { + let (fflags, elem) : (bits_fflags, bits('o)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_f16ToI32(0b001, vs2_val[i]), + 32 => riscv_f32ToI64(0b001, vs2_val[i]) + }; + write_fflags(fflags); + elem + } + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vfwunary0_mnemonic : vfwunary0 <-> string = { + FWV_CVT_XU_F <-> "vfwcvt.xu.f.v", + FWV_CVT_X_F <-> "vfwcvt.x.f.v", + FWV_CVT_F_XU <-> "vfwcvt.f.xu.v", + FWV_CVT_F_X <-> "vfwcvt.f.x.v", + FWV_CVT_F_F <-> "vfwcvt.f.f.v", + FWV_CVT_RTZ_XU_F <-> "vfwcvt.rtz.xu.f.v", + FWV_CVT_RTZ_X_F <-> "vfwcvt.rtz.x.f.v" +} + +mapping clause assembly = VFWUNARY0(vm, vs2, vfwunary0, vd) + <-> vfwunary0_mnemonic(vfwunary0) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************* OPFVV (VFUNARY0 Narrowing) ************************** */ +union clause ast = VFNUNARY0 : (bits(1), regidx, vfnunary0, regidx) + +mapping encdec_vfnunary0_vs1 : vfnunary0 <-> bits(5) = { + FNV_CVT_XU_F <-> 0b10000, + FNV_CVT_X_F <-> 0b10001, + FNV_CVT_F_XU <-> 0b10010, + FNV_CVT_F_X <-> 0b10011, + FNV_CVT_F_F <-> 0b10100, + FNV_CVT_ROD_F_F <-> 0b10101, + FNV_CVT_RTZ_XU_F <-> 0b10110, + FNV_CVT_RTZ_X_F <-> 0b10111 +} + +mapping clause encdec = VFNUNARY0(vm, vs2, vfnunary0, vd) if haveVExt() + <-> 0b010010 @ vm @ vs2 @ encdec_vfnunary0_vs1(vfnunary0) @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VFNUNARY0(vm, vs2, vfnunary0, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow_widen, LMUL_pow)) + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match vfnunary0 { + FNV_CVT_XU_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => riscv_f16ToUi8(rm_3b, vs2_val[i]), + 16 => riscv_f32ToUi16(rm_3b, vs2_val[i]), + 32 => riscv_f64ToUi32(rm_3b, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FNV_CVT_X_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => riscv_f16ToI8(rm_3b, vs2_val[i]), + 16 => riscv_f32ToI16(rm_3b, vs2_val[i]), + 32 => riscv_f64ToI32(rm_3b, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FNV_CVT_F_XU => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_ui32ToF16(rm_3b, vs2_val[i]), + 32 => riscv_ui64ToF32(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FNV_CVT_F_X => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_i32ToF16(rm_3b, vs2_val[i]), + 32 => riscv_i64ToF32(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FNV_CVT_F_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_f32ToF16(rm_3b, vs2_val[i]), + 32 => riscv_f64ToF32(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FNV_CVT_ROD_F_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => { handle_illegal(); return RETIRE_FAIL }, + 16 => riscv_f32ToF16(0b110, vs2_val[i]), + 32 => riscv_f64ToF32(0b110, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FNV_CVT_RTZ_XU_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => riscv_f16ToUi8(0b001, vs2_val[i]), + 16 => riscv_f32ToUi16(0b001, vs2_val[i]), + 32 => riscv_f64ToUi32(0b001, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FNV_CVT_RTZ_X_F => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 8 => riscv_f16ToI8(0b001, vs2_val[i]), + 16 => riscv_f32ToI16(0b001, vs2_val[i]), + 32 => riscv_f64ToI32(0b001, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vfnunary0_mnemonic : vfnunary0 <-> string = { + FNV_CVT_XU_F <-> "vfncvt.xu.f.w", + FNV_CVT_X_F <-> "vfncvt.x.f.w", + FNV_CVT_F_XU <-> "vfncvt.f.xu.w", + FNV_CVT_F_X <-> "vfncvt.f.x.w", + FNV_CVT_F_F <-> "vfncvt.f.f.w", + FNV_CVT_ROD_F_F <-> "vfncvt.rod.f.f.w", + FNV_CVT_RTZ_XU_F <-> "vfncvt.rtz.xu.f.w", + FNV_CVT_RTZ_X_F <-> "vfncvt.rtz.x.f.w" +} + +mapping clause assembly = VFNUNARY0(vm, vs2, vfnunary0, vd) + <-> vfnunary0_mnemonic(vfnunary0) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ****************************** OPFVV (VFUNARY1) ******************************* */ +union clause ast = VFUNARY1 : (bits(1), regidx, vfunary1, regidx) + +mapping encdec_vfunary1_vs1 : vfunary1 <-> bits(5) = { + FVV_VSQRT <-> 0b00000, + FVV_VRSQRT7 <-> 0b00100, + FVV_VREC7 <-> 0b00101, + FVV_VCLASS <-> 0b10000 +} + +mapping clause encdec = VFUNARY1(vm, vs2, vfunary1, vd) if haveVExt() + <-> 0b010011 @ vm @ vs2 @ encdec_vfunary1_vs1(vfunary1) @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VFUNARY1(vm, vs2, vfunary1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_normal(vd, vm, SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match vfunary1 { + FVV_VSQRT => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16Sqrt(rm_3b, vs2_val[i]), + 32 => riscv_f32Sqrt(rm_3b, vs2_val[i]), + 64 => riscv_f64Sqrt(rm_3b, vs2_val[i]) + }; + write_fflags(fflags); + elem + }, + FVV_VRSQRT7 => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16Rsqrte7(rm_3b, vs2_val[i]), + 32 => riscv_f32Rsqrte7(rm_3b, vs2_val[i]), + 64 => riscv_f64Rsqrte7(rm_3b, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FVV_VREC7 => { + let (fflags, elem) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16Recip7(rm_3b, vs2_val[i]), + 32 => riscv_f32Recip7(rm_3b, vs2_val[i]), + 64 => riscv_f64Recip7(rm_3b, vs2_val[i]) + }; + accrue_fflags(fflags); + elem + }, + FVV_VCLASS => fp_class(vs2_val[i]) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vfunary1_mnemonic : vfunary1 <-> string = { + FVV_VSQRT <-> "vfsqrt.v", + FVV_VRSQRT7 <-> "vfrsqrt7.v", + FVV_VREC7 <-> "vfrec7.v", + FVV_VCLASS <-> "vfclass.v" +} + +mapping clause assembly = VFUNARY1(vm, vs2, vfunary1, vd) + <-> vfunary1_mnemonic(vfunary1) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ****************************** OPFVV (VWFUNARY0) ****************************** */ +union clause ast = VFMVFS : (regidx, regidx) + +mapping clause encdec = VFMVFS(vs2, rd) if haveVExt() + <-> 0b010000 @ 0b1 @ vs2 @ 0b00000 @ 0b001 @ rd @ 0b1010111 if haveVExt() + +function clause execute(VFMVFS(vs2, rd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let num_elem = get_num_elem(0, SEW); + + if illegal_fp_vd_unmasked(SEW, rm_3b) | SEW > sizeof(flen) + then { handle_illegal(); return RETIRE_FAIL }; + assert(num_elem > 0 & SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, 0, vs2); + match 'm { + 16 => F_H(rd) = vs2_val[0], + 32 => F_S(rd) = vs2_val[0], + 64 => F_D(rd) = vs2_val[0] + }; + vstart = zeros(); + + RETIRE_SUCCESS +} + +mapping clause assembly = VFMVFS(vs2, rd) + <-> "vfmv.f.s" ^ spc() ^ freg_name(rd) ^ sep() ^ vreg_name(vs2) + +/* ******************************* OPFVF (VFtype) ******************************** */ +union clause ast = FVFTYPE : (fvffunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fvffunct6 : fvffunct6 <-> bits(6) = { + VF_VADD <-> 0b000000, + VF_VSUB <-> 0b000010, + VF_VMIN <-> 0b000100, + VF_VMAX <-> 0b000110, + VF_VSGNJ <-> 0b001000, + VF_VSGNJN <-> 0b001001, + VF_VSGNJX <-> 0b001010, + VF_VSLIDE1UP <-> 0b001110, + VF_VSLIDE1DOWN <-> 0b001111, + VF_VDIV <-> 0b100000, + VF_VRDIV <-> 0b100001, + VF_VMUL <-> 0b100100, + VF_VRSUB <-> 0b100111 +} + +mapping clause encdec = FVFTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_fvffunct6(funct6) @ vm @ vs2 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FVFTYPE(funct6, vm, vs2, rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_normal(vd, vm, SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VF_VADD => fp_add(rm_3b, vs2_val[i], rs1_val), + VF_VSUB => fp_sub(rm_3b, vs2_val[i], rs1_val), + VF_VRSUB => fp_sub(rm_3b, rs1_val, vs2_val[i]), + VF_VMIN => fp_min(vs2_val[i], rs1_val), + VF_VMAX => fp_max(vs2_val[i], rs1_val), + VF_VMUL => fp_mul(rm_3b, vs2_val[i], rs1_val), + VF_VDIV => fp_div(rm_3b, vs2_val[i], rs1_val), + VF_VRDIV => fp_div(rm_3b, rs1_val, vs2_val[i]), + VF_VSGNJ => [rs1_val['m - 1]] @ vs2_val[i][('m - 2)..0], + VF_VSGNJN => (0b1 ^ [rs1_val['m - 1]]) @ vs2_val[i][('m - 2)..0], + VF_VSGNJX => ([vs2_val[i]['m - 1]] ^ [rs1_val['m - 1]]) @ vs2_val[i][('m - 2)..0], + VF_VSLIDE1UP => { + if vs2 == vd then { handle_illegal(); return RETIRE_FAIL }; + if i == 0 then rs1_val else vs2_val[i - 1] + }, + VF_VSLIDE1DOWN => { + let last_elem = get_end_element(); + assert(last_elem < num_elem); + if i < last_elem then vs2_val[i + 1] else rs1_val + } + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fvftype_mnemonic : fvffunct6 <-> string = { + VF_VADD <-> "vfadd.vf", + VF_VSUB <-> "vfsub.vf", + VF_VMIN <-> "vfmin.vf", + VF_VMAX <-> "vfmax.vf", + VF_VSGNJ <-> "vfsgnj.vf", + VF_VSGNJN <-> "vfsgnjn.vf", + VF_VSGNJX <-> "vfsgnjx.vf", + VF_VSLIDE1UP <-> "vfslide1up.vf", + VF_VSLIDE1DOWN <-> "vfslide1down.vf", + VF_VDIV <-> "vfdiv.vf", + VF_VRDIV <-> "vfrdiv.vf", + VF_VMUL <-> "vfmul.vf", + VF_VRSUB <-> "vfrsub.vf" +} + +mapping clause assembly = FVFTYPE(funct6, vm, vs2, rs1, vd) + <-> fvftype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ************************* OPFVF (VFtype Multiply-Add) ************************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = FVFMATYPE : (fvfmafunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fvfmafunct6 : fvfmafunct6 <-> bits(6) = { + VF_VMADD <-> 0b101000, + VF_VNMADD <-> 0b101001, + VF_VMSUB <-> 0b101010, + VF_VNMSUB <-> 0b101011, + VF_VMACC <-> 0b101100, + VF_VNMACC <-> 0b101101, + VF_VMSAC <-> 0b101110, + VF_VNMSAC <-> 0b101111 +} + +mapping clause encdec = FVFMATYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_fvfmafunct6(funct6) @ vm @ vs2 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FVFMATYPE(funct6, vm, vs2, rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_normal(vd, vm, SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VF_VMACC => fp_muladd(rm_3b, rs1_val, vs2_val[i], vd_val[i]), + VF_VNMACC => fp_nmulsub(rm_3b, rs1_val, vs2_val[i], vd_val[i]), + VF_VMSAC => fp_mulsub(rm_3b, rs1_val, vs2_val[i], vd_val[i]), + VF_VNMSAC => fp_nmuladd(rm_3b, rs1_val, vs2_val[i], vd_val[i]), + VF_VMADD => fp_muladd(rm_3b, rs1_val, vd_val[i], vs2_val[i]), + VF_VNMADD => fp_nmulsub(rm_3b, rs1_val, vd_val[i], vs2_val[i]), + VF_VMSUB => fp_mulsub(rm_3b, rs1_val, vd_val[i], vs2_val[i]), + VF_VNMSUB => fp_nmuladd(rm_3b, rs1_val, vd_val[i], vs2_val[i]) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fvfmatype_mnemonic : fvfmafunct6 <-> string = { + VF_VMADD <-> "vfmadd.vf", + VF_VNMADD <-> "vfnmadd.vf", + VF_VMSUB <-> "vfmsub.vf", + VF_VNMSUB <-> "vfnmsub.vf", + VF_VMACC <-> "vfmacc.vf", + VF_VNMACC <-> "vfnmacc.vf", + VF_VMSAC <-> "vfmsac.vf", + VF_VNMSAC <-> "vfnmsac.vf" +} + +mapping clause assembly = FVFMATYPE(funct6, vm, vs2, rs1, vd) + <-> fvfmatype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ reg_name(rs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* *************************** OPFVF (VFTYPE Widening) *************************** */ +union clause ast = FWVFTYPE : (fwvffunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fwvffunct6 : fwvffunct6 <-> bits(6) = { + FWVF_VADD <-> 0b110000, + FWVF_VSUB <-> 0b110010, + FWVF_VMUL <-> 0b111000 +} + +mapping clause encdec = FWVFTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_fwvffunct6(funct6) @ vm @ vs2 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FWVFTYPE(funct6, vm, vs2, rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 16 & SEW_widen <= 64); + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FWVF_VADD => fp_add(rm_3b, fp_widen(vs2_val[i]), fp_widen(rs1_val)), + FWVF_VSUB => fp_sub(rm_3b, fp_widen(vs2_val[i]), fp_widen(rs1_val)), + FWVF_VMUL => fp_mul(rm_3b, fp_widen(vs2_val[i]), fp_widen(rs1_val)) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fwvftype_mnemonic : fwvffunct6 <-> string = { + FWVF_VADD <-> "vfwadd.vf", + FWVF_VSUB <-> "vfwsub.vf", + FWVF_VMUL <-> "vfwmul.vf" +} + +mapping clause assembly = FWVFTYPE(funct6, vm, vs2, rs1, vd) + <-> fwvftype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ******************** OPFVF (VFtype Widening Multiply-Add) ********************* */ +/* Multiply-Add instructions switch the order of source operands in assembly (vs1/rs1 before vs2) */ +union clause ast = FWVFMATYPE : (fwvfmafunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fwvfmafunct6 : fwvfmafunct6 <-> bits(6) = { + FWVF_VMACC <-> 0b111100, + FWVF_VNMACC <-> 0b111101, + FWVF_VMSAC <-> 0b111110, + FWVF_VNMSAC <-> 0b111111 +} + +mapping clause encdec = FWVFMATYPE(funct6, vm, rs1, vs2, vd) if haveVExt() + <-> encdec_fwvfmafunct6(funct6) @ vm @ vs2 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FWVFMATYPE(funct6, vm, rs1, vs2, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) | + not(valid_reg_overlap(vs2, vd, LMUL_pow, LMUL_pow_widen)) + then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 16 & SEW_widen <= 64); + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FWVF_VMACC => fp_muladd(rm_3b, fp_widen(rs1_val), fp_widen(vs2_val[i]), vd_val[i]), + FWVF_VNMACC => fp_nmulsub(rm_3b, fp_widen(rs1_val), fp_widen(vs2_val[i]), vd_val[i]), + FWVF_VMSAC => fp_mulsub(rm_3b, fp_widen(rs1_val), fp_widen(vs2_val[i]), vd_val[i]), + FWVF_VNMSAC => fp_nmuladd(rm_3b, fp_widen(rs1_val), fp_widen(vs2_val[i]), vd_val[i]) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fwvfmatype_mnemonic : fwvfmafunct6 <-> string = { + FWVF_VMACC <-> "vfwmacc.vf", + FWVF_VNMACC <-> "vfwnmacc.vf", + FWVF_VMSAC <-> "vfwmsac.vf", + FWVF_VNMSAC <-> "vfwnmsac.vf" +} + +mapping clause assembly = FWVFMATYPE(funct6, vm, rs1, vs2, vd) + <-> fwvfmatype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ reg_name(rs1) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* *************************** OPFVF (WFTYPE Widening) *************************** */ +union clause ast = FWFTYPE : (fwffunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fwffunct6 : fwffunct6 <-> bits(6) = { + FWF_VADD <-> 0b110100, + FWF_VSUB <-> 0b110110 +} + +mapping clause encdec = FWFTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_fwffunct6(funct6) @ vm @ vs2 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FWFTYPE(funct6, vm, vs2, rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + + if illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_widen, LMUL_pow_widen) + then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 16 & SEW_widen <= 64); + + let 'n = num_elem; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vs2_val : vector('n, dec, bits('o)) = read_vreg(num_elem, SEW_widen, LMUL_pow_widen, vs2); + result : vector('n, dec, bits('o)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW_widen, LMUL_pow_widen, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + FWF_VADD => fp_add(rm_3b, vs2_val[i], fp_widen(rs1_val)), + FWF_VSUB => fp_sub(rm_3b, vs2_val[i], fp_widen(rs1_val)) + } + } + }; + + write_vreg(num_elem, SEW_widen, LMUL_pow_widen, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fwftype_mnemonic : fwffunct6 <-> string = { + FWF_VADD <-> "vfwadd.wf", + FWF_VSUB <-> "vfwsub.wf" +} + +mapping clause assembly = FWFTYPE(funct6, vm, vs2, rs1, vd) + <-> fwftype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ************************** OPFVF (Merge Instruction) ************************** */ +/* This instruction operates on all body elements regardless of mask value */ +union clause ast = VFMERGE : (regidx, regidx, regidx) + +mapping clause encdec = VFMERGE(vs2, rs1, vd) if haveVExt() + <-> 0b010111 @ 0b0 @ vs2 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VFMERGE(vs2, rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let start_element = get_start_element(); + let end_element = get_end_element(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); /* max(VLMAX,VLEN/SEW)) */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / (0 - LMUL_pow); /* VLMAX */ + + if illegal_fp_vd_masked(vd, SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, 0b00000); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + + let tail_ag : agtype = get_vtype_vta(); + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + result[i] = vd_val[i] + } else if i > end_element | i >= real_num_elem then { + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + } + } else { + /* the merge operates on all body elements */ + result[i] = if vm_val[i] then rs1_val else vs2_val[i] + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VFMERGE(vs2, rs1, vd) + <-> "vfmerge.vfm" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ sep() ^ "v0" + +/* ************************** OPFVF (Move Instruction) *************************** */ +/* This instruction shares the encoding with vfmerge.vfm, but with vm=1 and vs2=v0 */ +union clause ast = VFMV : (regidx, regidx) + +mapping clause encdec = VFMV(rs1, vd) if haveVExt() + <-> 0b010111 @ 0b1 @ 0b00000 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VFMV(rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_vd_unmasked(SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b1, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then result[i] = rs1_val + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VFMV(rs1, vd) + <-> "vfmv.v.f" ^ spc() ^ vreg_name(vd) ^ sep() ^ reg_name(rs1) + +/* ****************************** OPFVF (VRFUNARY0) ****************************** */ +union clause ast = VFMVSF : (regidx, regidx) + +mapping clause encdec = VFMVSF(rs1, vd) if haveVExt() + <-> 0b010000 @ 0b1 @ 0b00000 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VFMVSF(rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let num_elem = get_num_elem(0, SEW); + + if illegal_fp_vd_unmasked(SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(num_elem > 0 & SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, 0b1, 0b00000); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, 0, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, 0, vd_val, vm_val); + + /* one body element */ + if mask[0] then result[0] = rs1_val; + + /* others treated as tail elements */ + let tail_ag : agtype = get_vtype_vta(); + foreach (i from 1 to (num_elem - 1)) { + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + } + }; + + write_vreg(num_elem, SEW, 0, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VFMVSF(rs1, vd) + <-> "vfmv.s.f" ^ spc() ^ vreg_name(vd) ^ sep() ^ freg_name(rs1) diff --git a/model/riscv_insts_vext_mask.sail b/model/riscv_insts_vext_mask.sail new file mode 100755 index 000000000..bb4594f7e --- /dev/null +++ b/model/riscv_insts_vext_mask.sail @@ -0,0 +1,388 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements part of the vector extension. */ +/* Chapter 15: vector mask instructions */ +/* ******************************************************************************* */ + +/* ******************************* OPMVV (MMTYPE) ******************************** */ +union clause ast = MMTYPE : (mmfunct6, regidx, regidx, regidx) + +mapping encdec_mmfunct6 : mmfunct6 <-> bits(6) = { + MM_VMAND <-> 0b011001, + MM_VMNAND <-> 0b011101, + MM_VMANDNOT <-> 0b011000, + MM_VMXOR <-> 0b011011, + MM_VMOR <-> 0b011010, + MM_VMNOR <-> 0b011110, + MM_VMORNOT <-> 0b011100, + MM_VMXNOR <-> 0b011111 +} + +mapping clause encdec = MMTYPE(funct6, vs2, vs1, vd) if haveVExt() + <-> encdec_mmfunct6(funct6) @ 0b1 @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(MMTYPE(funct6, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = unsigned(vlenb) * 8; + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vs1_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs1); + let vs2_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_carry(num_elem, SEW, 0, vd_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + MM_VMAND => vs2_val[i] & vs1_val[i], + MM_VMNAND => not(vs2_val[i] & vs1_val[i]), + MM_VMANDNOT => vs2_val[i] & not(vs1_val[i]), + MM_VMXOR => vs2_val[i] != vs1_val[i], + MM_VMOR => vs2_val[i] | vs1_val[i], + MM_VMNOR => not(vs2_val[i] | vs1_val[i]), + MM_VMORNOT => vs2_val[i] | not(vs1_val[i]), + MM_VMXNOR => vs2_val[i] == vs1_val[i] + } + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping mmtype_mnemonic : mmfunct6 <-> string = { + MM_VMAND <-> "vmand.mm", + MM_VMNAND <-> "vmnand.mm", + MM_VMANDNOT <-> "vmandnot.mm", + MM_VMXOR <-> "vmxor.mm", + MM_VMOR <-> "vmor.mm", + MM_VMNOR <-> "vmnor.mm", + MM_VMORNOT <-> "vmornot.mm", + MM_VMXNOR <-> "vmxnor.mm" +} + +mapping clause assembly = MMTYPE(funct6, vs2, vs1, vd) + <-> mmtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) + +/* ************************* OPMVV (vpopc in VWXUNARY0) ************************** */ +union clause ast = VCPOP_M : (bits(1), regidx, regidx) + +mapping clause encdec = VCPOP_M(vm, vs2, rd) if haveVExt() + <-> 0b010000 @ vm @ vs2 @ 0b10000 @ 0b010 @ rd @ 0b1010111 if haveVExt() + +function clause execute(VCPOP_M(vm, vs2, rd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = unsigned(vlenb) * 8; + + if illegal_vd_unmasked() | not(assert_vstart(0)) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs2); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, 0, vs2_val, vm_val); + + count : nat = 0; + foreach (i from 0 to (num_elem - 1)) { + if mask[i] & vs2_val[i] then count = count + 1; + }; + + X(rd) = to_bits(sizeof(xlen), count); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VCPOP_M(vm, vs2, rd) + <-> "vpopc.m" ^ spc() ^ reg_name(rd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************* OPMVV (vfirst in VWXUNARY0) ************************* */ +union clause ast = VFIRST_M : (bits(1), regidx, regidx) + +mapping clause encdec = VFIRST_M(vm, vs2, rd) if haveVExt() + <-> 0b010000 @ vm @ vs2 @ 0b10001 @ 0b010 @ rd @ 0b1010111 if haveVExt() + +function clause execute(VFIRST_M(vm, vs2, rd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = unsigned(vlenb) * 8; + + if illegal_vd_unmasked() | not(assert_vstart(0)) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs2); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, 0, vs2_val, vm_val); + + index : int = -1; + foreach (i from 0 to (num_elem - 1)) { + if index == -1 then { + if mask[i] & vs2_val[i] then index = i; + }; + }; + + X(rd) = to_bits(sizeof(xlen), index); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VFIRST_M(vm, vs2, rd) + <-> "vfirst.m" ^ spc() ^ reg_name(rd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************** OPMVV (vmsbf in VMUNARY0) ************************** */ +union clause ast = VMSBF_M : (bits(1), regidx, regidx) + +mapping clause encdec = VMSBF_M(vm, vs2, vd) if haveVExt() + <-> 0b010100 @ vm @ vs2 @ 0b00001 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VMSBF_M(vm, vs2, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = unsigned(vlenb) * 8; + + if illegal_normal(vd, vm) | not(assert_vstart(0)) | vd == vs2 + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, 0, vd_val, vm_val); + + found_elem : bool = false; + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + if vs2_val[i] then found_elem = true; + result[i] = if found_elem then false else true + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VMSBF_M(vm, vs2, vd) + <-> "vmsbf.m" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************** OPMVV (vmsif in VMUNARY0) ************************** */ +union clause ast = VMSIF_M : (bits(1), regidx, regidx) + +mapping clause encdec = VMSIF_M(vm, vs2, vd) if haveVExt() + <-> 0b010100 @ vm @ vs2 @ 0b00011 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VMSIF_M(vm, vs2, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = unsigned(vlenb) * 8; + + if illegal_normal(vd, vm) | not(assert_vstart(0)) | vd == vs2 + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, 0, vd_val, vm_val); + + found_elem : bool = false; + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = if found_elem then false else true; + if vs2_val[i] then found_elem = true + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VMSIF_M(vm, vs2, vd) + <-> "vmsif.m" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************** OPMVV (vmsof in VMUNARY0) ************************** */ +union clause ast = VMSOF_M : (bits(1), regidx, regidx) + +mapping clause encdec = VMSOF_M(vm, vs2, vd) if haveVExt() + <-> 0b010100 @ vm @ vs2 @ 0b00010 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VMSOF_M(vm, vs2, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = unsigned(vlenb) * 8; + + if illegal_normal(vd, vm) | not(assert_vstart(0)) | vd == vs2 + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, 0, vd_val, vm_val); + + found_elem : bool = false; + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + if vs2_val[i] & not(found_elem) then { + result[i] = true; + found_elem = true + } else { + result[i] = false + } + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VMSOF_M(vm, vs2, vd) + <-> "vmsof.m" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* ************************** OPMVV (viota in VMUNARY0) ************************** */ +union clause ast = VIOTA_M : (bits(1), regidx, regidx) + +mapping clause encdec = VIOTA_M(vm, vs2, vd) if haveVExt() + <-> 0b010100 @ vm @ vs2 @ 0b10000 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VIOTA_M(vm, vs2, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) | not(assert_vstart(0)) | vd == vs2 + then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs2_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + sum : int = 0; + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = to_bits(SEW, sum); + if vs2_val[i] then sum = sum + 1 + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VIOTA_M(vm, vs2, vd) + <-> "viota.m" ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ maybe_vmask(vm) + +/* *************************** OPMVV (vid in VMUNARY0) *************************** */ +union clause ast = VID_V : (bits(1), regidx) + +mapping clause encdec = VID_V(vm, vd) if haveVExt() + <-> 0b010100 @ vm @ 0b00000 @ 0b10001 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VID_V(vm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_normal(vd, vm) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then result[i] = to_bits(SEW, i) + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping clause assembly = VID_V(vm, vd) + <-> "vid.v" ^ spc() ^ vreg_name(vd) ^ maybe_vmask(vm) diff --git a/model/riscv_insts_vext_mem.sail b/model/riscv_insts_vext_mem.sail new file mode 100644 index 000000000..292d98f02 --- /dev/null +++ b/model/riscv_insts_vext_mem.sail @@ -0,0 +1,939 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements part of the vector extension. */ +/* Chapter 7: Vector Loads and Stores */ +/* ******************************************************************************* */ + +mapping nfields_int : bits(3) <-> {|1, 2, 3, 4, 5, 6, 7, 8|} = { + 0b000 <-> 1, + 0b001 <-> 2, + 0b010 <-> 3, + 0b011 <-> 4, + 0b100 <-> 5, + 0b101 <-> 6, + 0b110 <-> 7, + 0b111 <-> 8 +} + +mapping nfields_string : bits(3) <-> string = { + 0b000 <-> "", + 0b001 <-> "seg2", + 0b010 <-> "seg3", + 0b011 <-> "seg4", + 0b100 <-> "seg5", + 0b101 <-> "seg6", + 0b110 <-> "seg7", + 0b111 <-> "seg8" +} + +mapping vlewidth_bitsnumberstr : vlewidth <-> string = { + VLE8 <-> "8", + VLE16 <-> "16", + VLE32 <-> "32", + VLE64 <-> "64" +} + +mapping encdec_vlewidth : vlewidth <-> bits(3) = { + VLE8 <-> 0b000, + VLE16 <-> 0b101, + VLE32 <-> 0b110, + VLE64 <-> 0b111 +} + +mapping vlewidth_bytesnumber : vlewidth <-> {|1, 2, 4, 8|} = { + VLE8 <-> 1, + VLE16 <-> 2, + VLE32 <-> 4, + VLE64 <-> 8 +} + +mapping vlewidth_pow : vlewidth <-> {|3, 4, 5, 6|} = { + VLE8 <-> 3, + VLE16 <-> 4, + VLE32 <-> 5, + VLE64 <-> 6 +} + +mapping bytes_wordwidth : {|1, 2, 4, 8|} <-> word_width = { + 1 <-> BYTE, + 2 <-> HALF, + 4 <-> WORD, + 8 <-> DOUBLE +} + +/* ******************** Vector Load Unit-Stride Normal & Segment (mop=0b00, lumop=0b00000) ********************* */ +union clause ast = VLSEGTYPE : (bits(3), bits(1), regidx, vlewidth, regidx) + +mapping clause encdec = VLSEGTYPE(nf, vm, rs1, width, vd) if haveVExt() + <-> nf @ 0b0 @ 0b00 @ vm @ 0b00000 @ rs1 @ encdec_vlewidth(width) @ vd @ 0b0000111 if haveVExt() + +val process_vlseg : forall 'f 'b 'n 'p, (0 < 'f & 'f <= 8) & ('b in {1, 2, 4, 8}) & ('n >= 0). (int('f), bits(1), regidx, int('b), regidx, int('p), int('n)) -> Retired effect {escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vlseg (nf, vm, vd, load_width_bytes, rs1, EMUL_pow, num_elem) = { + let EMUL_reg : int = if EMUL_pow <= 0 then 1 else int_power(2, EMUL_pow); + let width_type : word_width = bytes_wordwidth(load_width_bytes); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_seg : vector('n, dec, bits('f * 'b * 8)) = read_vreg_seg(num_elem, load_width_bytes * 8, EMUL_pow, nf, vd); + + let (result, mask) = init_masked_result(num_elem, nf * load_width_bytes * 8, EMUL_pow, vd_seg, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { /* active segments */ + vstart = to_bits(16, i); + foreach (j from 0 to (nf - 1)) { + let elem_offset = (i * nf + j) * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Read(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_Load_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Read(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + match mem_read(Read(Data), paddr, load_width_bytes, false, false, false) { + MemValue(elem) => write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j * EMUL_reg), elem), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + } else { /* prestart, masked or tail segments */ + foreach (j from 0 to (nf - 1)) { + let skipped_elem = (result[i] >> (j * load_width_bytes * 8))[(load_width_bytes * 8 - 1) .. 0]; + write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j * EMUL_reg), skipped_elem) + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VLSEGTYPE(nf, vm, rs1, width, vd)) = { + let load_width_bytes = vlewidth_bytesnumber(width); + let EEW = load_width_bytes * 8; + let EEW_pow = vlewidth_pow(width); + let SEW_pow = get_sew_pow(); + let LMUL_pow = get_lmul_pow(); + let EMUL_pow = EEW_pow - SEW_pow + LMUL_pow; + let num_elem = get_num_elem(EMUL_pow, EEW); /* # of element of each register group */ + let nf_int = nfields_int(nf); + + if illegal_load(vd, vm, nf_int, EEW, EMUL_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vlseg(nf_int, vm, vd, load_width_bytes, rs1, EMUL_pow, num_elem) +} + +mapping clause assembly = VLSEGTYPE(nf, vm, rs1, width, vd) + <-> "vl" ^ nfields_string(nf) ^ "e" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vd) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ maybe_vmask(vm) + +/* ************ Vector Load Unit-Stride Normal & Segment Fault-Only-First (mop=0b00, lumop=0b10000) ************ */ +union clause ast = VLSEGFFTYPE : (bits(3), bits(1), regidx, vlewidth, regidx) + +mapping clause encdec = VLSEGFFTYPE(nf, vm, rs1, width, vd) if haveVExt() + <-> nf @ 0b0 @ 0b00 @ vm @ 0b10000 @ rs1 @ encdec_vlewidth(width) @ vd @ 0b0000111 if haveVExt() + +val process_vlsegff : forall 'f 'b 'n 'p, (0 < 'f & 'f <= 8) & ('b in {1, 2, 4, 8}) & ('n >= 0). (int('f), bits(1), regidx, int('b), regidx, int('p), int('n)) -> Retired effect {escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vlsegff (nf, vm, vd, load_width_bytes, rs1, EMUL_pow, num_elem) = { + let EMUL_reg : int = if EMUL_pow <= 0 then 1 else int_power(2, EMUL_pow); + let width_type : word_width = bytes_wordwidth(load_width_bytes); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_seg : vector('n, dec, bits('f * 'b * 8)) = read_vreg_seg(num_elem, load_width_bytes * 8, EMUL_pow, nf, vd); + let tail_ag : agtype = get_vtype_vta(); + + let (result, mask) = init_masked_result(num_elem, nf * load_width_bytes * 8, EMUL_pow, vd_seg, vm_val); + + trimmed : bool = false; + foreach (i from 0 to (num_elem - 1)) { + if not(trimmed) then { + if vm_val[i] then { /* active segments */ + foreach (j from 0 to (nf - 1)) { + let elem_offset = (i * nf + j) * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Read(Data), width_type) { + Ext_DataAddr_Error(e) => { + if i == 0 then { ext_handle_data_check_error(e); return RETIRE_FAIL } + else { + vl = to_bits(sizeof(xlen), i); + print_reg("CSR vl <- " ^ BitStr(vl)); + trimmed = true + } + }, + Ext_DataAddr_OK(vaddr) => { + if check_misaligned(vaddr, width_type) then { + if i == 0 then { handle_mem_exception(vaddr, E_Load_Addr_Align()); return RETIRE_FAIL } + else { + vl = to_bits(sizeof(xlen), i); + print_reg("CSR vl <- " ^ BitStr(vl)); + trimmed = true + } + } else match translateAddr(vaddr, Read(Data)) { + TR_Failure(e, _) => { + if i == 0 then { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + else { + vl = to_bits(sizeof(xlen), i); + print_reg("CSR vl <- " ^ BitStr(vl)); + trimmed = true + } + }, + TR_Address(paddr, _) => { + match mem_read(Read(Data), paddr, load_width_bytes, false, false, false) { + MemValue(elem) => write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j * EMUL_reg), elem), + MemException(e) => { + if i == 0 then { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + else { + vl = to_bits(sizeof(xlen), i); + print_reg("CSR vl <- " ^ BitStr(vl)); + trimmed = true + } + } + } + } + } + } + } + } + } else { /* prestart, masked or tail segments */ + foreach (j from 0 to (nf - 1)) { + let skipped_elem = (result[i] >> (j * load_width_bytes * 8))[(load_width_bytes * 8 - 1) .. 0]; + write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j * EMUL_reg), skipped_elem) + } + } + } else { + /* if vl is trimmed, elements past the new vl are treated as tail elements */ + if tail_ag == AGNOSTIC then { + foreach (j from 0 to (nf - 1)) { + let skipped_elem = (vd_seg[i] >> (j * load_width_bytes * 8))[(load_width_bytes * 8 - 1) .. 0]; + write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j * EMUL_reg), skipped_elem) + } + /* TODO: configuration support for agnostic behavior */ + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VLSEGFFTYPE(nf, vm, rs1, width, vd)) = { + let load_width_bytes = vlewidth_bytesnumber(width); + let EEW = load_width_bytes * 8; + let EEW_pow = vlewidth_pow(width); + let SEW_pow = get_sew_pow(); + let LMUL_pow = get_lmul_pow(); + let EMUL_pow = EEW_pow - SEW_pow + LMUL_pow; + let num_elem = get_num_elem(EMUL_pow, EEW); + let nf_int = nfields_int(nf); + + if illegal_load(vd, vm, nf_int, EEW, EMUL_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vlsegff(nf_int, vm, vd, load_width_bytes, rs1, EMUL_pow, num_elem) +} + +mapping clause assembly = VLSEGFFTYPE(nf, vm, rs1, width, vd) + <-> "vl" ^ nfields_string(nf) ^ "e" ^ vlewidth_bitsnumberstr(width) ^ "ff.v" ^ spc() ^ vreg_name(vd) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ maybe_vmask(vm) + +/* ******************** Vector Store Unit-Stride Normal & Segment (mop=0b00, sumop=0b00000) ******************** */ +union clause ast = VSSEGTYPE : (bits(3), bits(1), regidx, vlewidth, regidx) + +mapping clause encdec = VSSEGTYPE(nf, vm, rs1, width, vs3) if haveVExt() + <-> nf @ 0b0 @ 0b00 @ vm @ 0b00000 @ rs1 @ encdec_vlewidth(width) @ vs3 @ 0b0100111 if haveVExt() + +val process_vsseg : forall 'f 'b 'n 'p, (0 < 'f & 'f <= 8) & ('b in {1, 2, 4, 8}) & ('n >= 0). (int('f), bits(1), regidx, int('b), regidx, int('p), int('n)) -> Retired effect {eamem, escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vsseg (nf, vm, vs3, load_width_bytes, rs1, EMUL_pow, num_elem) = { + let EMUL_reg : int = if EMUL_pow <= 0 then 1 else int_power(2, EMUL_pow); + let width_type : word_width = bytes_wordwidth(load_width_bytes); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs3_seg : vector('n, dec, bits('f * 'b * 8)) = read_vreg_seg(num_elem, load_width_bytes * 8, EMUL_pow, nf, vs3); + let mask : vector('n, dec, bool) = init_masked_source(num_elem, EMUL_pow, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if vm_val[i] then { /* active segments */ + vstart = to_bits(16, i); + foreach (j from 0 to (nf - 1)) { + let elem_offset = (i * nf + j) * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Write(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_SAMO_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Write(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + let eares : MemoryOpResult(unit) = mem_write_ea(paddr, load_width_bytes, false, false, false); + match (eares) { + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + MemValue(_) => { + let elem_val : bits('b * 8) = read_single_element(load_width_bytes * 8, i, vs3 + to_bits(5, j * EMUL_reg)); + let res : MemoryOpResult(bool) = mem_write_value(paddr, load_width_bytes, elem_val, false, false, false); + match (res) { + MemValue(true) => (), + MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + } + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VSSEGTYPE(nf, vm, rs1, width, vs3)) = { + let load_width_bytes = vlewidth_bytesnumber(width); + let EEW = load_width_bytes * 8; + let EEW_pow = vlewidth_pow(width); + let SEW_pow = get_sew_pow(); + let LMUL_pow = get_lmul_pow(); + let EMUL_pow = EEW_pow - SEW_pow + LMUL_pow; + let num_elem = get_num_elem(EMUL_pow, EEW); + let nf_int = nfields_int(nf); + + if illegal_store(nf_int, EEW, EMUL_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vsseg(nf_int, vm, vs3, load_width_bytes, rs1, EMUL_pow, num_elem) +} + +mapping clause assembly = VSSEGTYPE(nf, vm, rs1, width, vs3) + <-> "vs" ^ nfields_string(nf) ^ "e" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vs3) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ maybe_vmask(vm) + +/* ****************************** Vector Load Strided Normal & Segment (mop=0b10) ****************************** */ +union clause ast = VLSSEGTYPE : (bits(3), bits(1), regidx, regidx, vlewidth, regidx) + +mapping clause encdec = VLSSEGTYPE(nf, vm, rs2, rs1, width, vd) if haveVExt() + <-> nf @ 0b0 @ 0b10 @ vm @ rs2 @ rs1 @ encdec_vlewidth(width) @ vd @ 0b0000111 if haveVExt() + +val process_vlsseg : forall 'f 'b 'n 'p, (0 < 'f & 'f <= 8) & ('b in {1, 2, 4, 8}) & ('n >= 0). (int('f), bits(1), regidx, int('b), regidx, regidx, int('p), int('n)) -> Retired effect {escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vlsseg (nf, vm, vd, load_width_bytes, rs1, rs2, EMUL_pow, num_elem) = { + let EMUL_reg : int = if EMUL_pow <= 0 then 1 else int_power(2, EMUL_pow); + let width_type : word_width = bytes_wordwidth(load_width_bytes); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_seg : vector('n, dec, bits('f * 'b * 8)) = read_vreg_seg(num_elem, load_width_bytes * 8, EMUL_pow, nf, vd); + let rs2_val : int = signed(get_scalar(rs2, sizeof(xlen))); + + let (result, mask) = init_masked_result(num_elem, nf * load_width_bytes * 8, EMUL_pow, vd_seg, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { /* active segments */ + vstart = to_bits(16, i); + foreach (j from 0 to (nf - 1)) { + let elem_offset = i * rs2_val + j * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Read(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_Load_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Read(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + match mem_read(Read(Data), paddr, load_width_bytes, false, false, false) { + MemValue(elem) => write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j * EMUL_reg), elem), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + } else { /* prestart, masked or tail segments */ + foreach (j from 0 to (nf - 1)) { + let skipped_elem = (result[i] >> (j * load_width_bytes * 8))[(load_width_bytes * 8 - 1) .. 0]; + write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j * EMUL_reg), skipped_elem) + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VLSSEGTYPE(nf, vm, rs2, rs1, width, vd)) = { + let load_width_bytes = vlewidth_bytesnumber(width); + let EEW = load_width_bytes * 8; + let EEW_pow = vlewidth_pow(width); + let SEW_pow = get_sew_pow(); + let LMUL_pow = get_lmul_pow(); + let EMUL_pow = EEW_pow - SEW_pow + LMUL_pow; + let num_elem = get_num_elem(EMUL_pow, EEW); + let nf_int = nfields_int(nf); + + if illegal_load(vd, vm, nf_int, EEW, EMUL_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vlsseg(nf_int, vm, vd, load_width_bytes, rs1, rs2, EMUL_pow, num_elem) +} + +mapping clause assembly = VLSSEGTYPE(nf, vm, rs2, rs1, width, vd) + <-> "vls" ^ nfields_string(nf) ^ "e" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vd) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ sep() ^ reg_name(rs2) ^ maybe_vmask(vm) + +/* ***************************** Vector Store Strided Normal & Segment (mop=0b10) ****************************** */ +union clause ast = VSSSEGTYPE : (bits(3), bits(1), regidx, regidx, vlewidth, regidx) + +mapping clause encdec = VSSSEGTYPE(nf, vm, rs2, rs1, width, vs3) if haveVExt() + <-> nf @ 0b0 @ 0b10 @ vm @ rs2 @ rs1 @ encdec_vlewidth(width) @ vs3 @ 0b0100111 if haveVExt() + +val process_vssseg : forall 'f 'b 'n 'p, (0 < 'f & 'f <= 8) & ('b in {1, 2, 4, 8}) & ('n >= 0). (int('f), bits(1), regidx, int('b), regidx, regidx, int('p), int('n)) -> Retired effect {eamem, escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vssseg (nf, vm, vs3, load_width_bytes, rs1, rs2, EMUL_pow, num_elem) = { + let EMUL_reg : int = if EMUL_pow <= 0 then 1 else int_power(2, EMUL_pow); + let width_type : word_width = bytes_wordwidth(load_width_bytes); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs3_seg : vector('n, dec, bits('f * 'b * 8)) = read_vreg_seg(num_elem, load_width_bytes * 8, EMUL_pow, nf, vs3); + let rs2_val : int = signed(get_scalar(rs2, sizeof(xlen))); + let mask : vector('n, dec, bool) = init_masked_source(num_elem, EMUL_pow, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { /* active segments */ + vstart = to_bits(16, i); + foreach (j from 0 to (nf - 1)) { + let elem_offset = i * rs2_val + j * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Write(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_SAMO_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Write(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + let eares : MemoryOpResult(unit) = mem_write_ea(paddr, load_width_bytes, false, false, false); + match (eares) { + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + MemValue(_) => { + let elem_val : bits('b * 8) = read_single_element(load_width_bytes * 8, i, vs3 + to_bits(5, j * EMUL_reg)); + let res : MemoryOpResult(bool) = mem_write_value(paddr, load_width_bytes, elem_val, false, false, false); + match (res) { + MemValue(true) => (), + MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + } + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VSSSEGTYPE(nf, vm, rs2, rs1, width, vs3)) = { + let load_width_bytes = vlewidth_bytesnumber(width); + let EEW = load_width_bytes * 8; + let EEW_pow = vlewidth_pow(width); + let SEW_pow = get_sew_pow(); + let LMUL_pow = get_lmul_pow(); + let EMUL_pow = EEW_pow - SEW_pow + LMUL_pow; + let num_elem = get_num_elem(EMUL_pow, EEW); + let nf_int = nfields_int(nf); + + if illegal_store(nf_int, EEW, EMUL_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vssseg(nf_int, vm, vs3, load_width_bytes, rs1, rs2, EMUL_pow, num_elem) +} + +mapping clause assembly = VSSSEGTYPE(nf, vm, rs2, rs1, width, vs3) + <-> "vss" ^ nfields_string(nf) ^ "e" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vs3) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ sep() ^ reg_name(rs2) ^ maybe_vmask(vm) + +/* ************************* Vector Load Indexed Unordered Normal & Segment (mop=0b01) ************************* */ +union clause ast = VLUXSEGTYPE : (bits(3), bits(1), regidx, regidx, vlewidth, regidx) + +mapping clause encdec = VLUXSEGTYPE(nf, vm, vs2, rs1, width, vd) if haveVExt() + <-> nf @ 0b0 @ 0b01 @ vm @ vs2 @ rs1 @ encdec_vlewidth(width) @ vd @ 0b0000111 if haveVExt() + +val process_vlxseg : forall 'f 'ib 'db 'ip 'dp 'n, (0 < 'f & 'f <= 8) & ('ib in {1, 2, 4, 8}) & ('db in {1, 2, 4, 8}) & ('n >= 0). (int('f), bits(1), regidx, int('ib), int('db), int('ip), int('dp), regidx, regidx, int('n), int) -> Retired effect {eamem, escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vlxseg (nf, vm, vd, EEW_index_bytes, EEW_data_bytes, EMUL_index_pow, EMUL_data_pow, rs1, vs2, num_elem, mop) = { + let EMUL_data_reg : int = if EMUL_data_pow <= 0 then 1 else int_power(2, EMUL_data_pow); + let width_type : word_width = bytes_wordwidth(EEW_data_bytes); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vd_seg : vector('n, dec, bits('f * 'db * 8)) = read_vreg_seg(num_elem, EEW_data_bytes * 8, EMUL_data_pow, nf, vd); + let vs2_val : vector('n, dec, bits('ib * 8)) = read_vreg(num_elem, EEW_index_bytes * 8, EMUL_index_pow, vs2); + + let (result, mask) = init_masked_result(num_elem, nf * EEW_data_bytes * 8, EMUL_data_pow, vd_seg, vm_val); + + /* currently mop = 1 (unordered) or 3 (ordered) do the same operations */ + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { /* active segments */ + vstart = to_bits(16, i); + foreach (j from 0 to (nf - 1)) { + let elem_offset : int = signed(vs2_val[i]) + j * EEW_data_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Read(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_Load_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Read(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + match mem_read(Read(Data), paddr, EEW_data_bytes, false, false, false) { + MemValue(elem) => write_single_element(EEW_data_bytes * 8, i, vd + to_bits(5, j * EMUL_data_reg), elem), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + } else { /* prestart, masked or tail segments */ + foreach (j from 0 to (nf - 1)) { + let skipped_elem = (result[i] >> (j * EEW_data_bytes * 8))[(EEW_data_bytes * 8 - 1) .. 0]; + write_single_element(EEW_data_bytes * 8, i, vd + to_bits(5, j * EMUL_data_reg), skipped_elem) + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VLUXSEGTYPE(nf, vm, vs2, rs1, width, vd)) = { + let EEW_index_pow = vlewidth_pow(width); + let EEW_index_bytes = vlewidth_bytesnumber(width); + let EEW_data_pow = get_sew_pow(); + let EEW_data_bytes = get_sew_bytes(); + let EMUL_data_pow = get_lmul_pow(); + let EMUL_index_pow = EEW_index_pow - EEW_data_pow + EMUL_data_pow; + let num_elem = get_num_elem(EMUL_data_pow, EEW_data_bytes * 8); + let nf_int = nfields_int(nf); + + if illegal_indexed_load(vd, vm, nf_int, EEW_index_bytes * 8, EMUL_index_pow, EMUL_data_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vlxseg(nf_int, vm, vd, EEW_index_bytes, EEW_data_bytes, EMUL_index_pow, EMUL_data_pow, rs1, vs2, num_elem, 1) +} + +mapping clause assembly = VLUXSEGTYPE(nf, vm, vs2, rs1, width, vd) + <-> "vlux" ^ nfields_string(nf) ^ "ei" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vd) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ sep() ^ reg_name(vs2) ^ maybe_vmask(vm) + +/* ************************** Vector Load Indexed Ordered Normal & Segment (mop=0b11) ************************** */ +union clause ast = VLOXSEGTYPE : (bits(3), bits(1), regidx, regidx, vlewidth, regidx) + +mapping clause encdec = VLOXSEGTYPE(nf, vm, vs2, rs1, width, vd) if haveVExt() + <-> nf @ 0b0 @ 0b11 @ vm @ vs2 @ rs1 @ encdec_vlewidth(width) @ vd @ 0b0000111 if haveVExt() + +function clause execute(VLOXSEGTYPE(nf, vm, vs2, rs1, width, vd)) = { + let EEW_index_pow = vlewidth_pow(width); + let EEW_index_bytes = vlewidth_bytesnumber(width); + let EEW_data_pow = get_sew_pow(); + let EEW_data_bytes = get_sew_bytes(); + let EMUL_data_pow = get_lmul_pow(); + let EMUL_index_pow = EEW_index_pow - EEW_data_pow + EMUL_data_pow; + let num_elem = get_num_elem(EMUL_data_pow, EEW_data_bytes * 8); + let nf_int = nfields_int(nf); + + if illegal_indexed_load(vd, vm, nf_int, EEW_index_bytes * 8, EMUL_index_pow, EMUL_data_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vlxseg(nf_int, vm, vd, EEW_index_bytes, EEW_data_bytes, EMUL_index_pow, EMUL_data_pow, rs1, vs2, num_elem, 3) +} + +mapping clause assembly = VLOXSEGTYPE(nf, vm, vs2, rs1, width, vd) + <-> "vlox" ^ nfields_string(nf) ^ "ei" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vd) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ sep() ^ reg_name(vs2) ^ maybe_vmask(vm) + +/* ************************ Vector Store Indexed Unordered Normal & Segment (mop=0b01) ************************* */ +union clause ast = VSUXSEGTYPE : (bits(3), bits(1), regidx, regidx, vlewidth, regidx) + +mapping clause encdec = VSUXSEGTYPE(nf, vm, vs2, rs1, width, vs3) if haveVExt() + <-> nf @ 0b0 @ 0b01 @ vm @ vs2 @ rs1 @ encdec_vlewidth(width) @ vs3 @ 0b0100111 if haveVExt() + +val process_vsxseg : forall 'f 'ib 'db 'ip 'dp 'n, (0 < 'f & 'f <= 8) & ('ib in {1, 2, 4, 8}) & ('db in {1, 2, 4, 8}) & ('n >= 0). (int('f), bits(1), regidx, int('ib), int('db), int('ip), int('dp), regidx, regidx, int('n), int) -> Retired effect {eamem, escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vsxseg (nf, vm, vs3, EEW_index_bytes, EEW_data_bytes, EMUL_index_pow, EMUL_data_pow, rs1, vs2, num_elem, mop) = { + let EMUL_data_reg : int = if EMUL_data_pow <= 0 then 1 else int_power(2, EMUL_data_pow); + let width_type : word_width = bytes_wordwidth(EEW_data_bytes); + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs3_seg : vector('n, dec, bits('f * 'db * 8)) = read_vreg_seg(num_elem, EEW_data_bytes * 8, EMUL_data_pow, nf, vs3); + let vs2_val : vector('n, dec, bits('ib * 8)) = read_vreg(num_elem, EEW_index_bytes * 8, EMUL_index_pow, vs2); + let mask : vector('n, dec, bool) = init_masked_source(num_elem, EMUL_data_pow, vm_val); + + /* currently mop = 1 (unordered) or 3 (ordered) do the same operations */ + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { /* active segments */ + vstart = to_bits(16, i); + foreach (j from 0 to (nf - 1)) { + let elem_offset : int = signed(vs2_val[i]) + j * EEW_data_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Write(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_SAMO_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Write(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + let eares : MemoryOpResult(unit) = mem_write_ea(paddr, EEW_data_bytes, false, false, false); + match (eares) { + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + MemValue(_) => { + let elem_val : bits('db * 8) = read_single_element(EEW_data_bytes * 8, i, vs3 + to_bits(5, j * EMUL_data_reg)); + let res : MemoryOpResult(bool) = mem_write_value(paddr, EEW_data_bytes, elem_val, false, false, false); + match (res) { + MemValue(true) => (), + MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + } + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VSUXSEGTYPE(nf, vm, vs2, rs1, width, vs3)) = { + let EEW_index_pow = vlewidth_pow(width); + let EEW_index_bytes = vlewidth_bytesnumber(width); + let EEW_data_pow = get_sew_pow(); + let EEW_data_bytes = get_sew_bytes(); + let EMUL_data_pow = get_lmul_pow(); + let EMUL_index_pow = EEW_index_pow - EEW_data_pow + EMUL_data_pow; + let num_elem = get_num_elem(EMUL_data_pow, EEW_data_bytes * 8); /* number of data and indices are the same */ + let nf_int = nfields_int(nf); + + if illegal_indexed_store(nf_int, EEW_index_bytes * 8, EMUL_index_pow, EMUL_data_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vsxseg(nf_int, vm, vs3, EEW_index_bytes, EEW_data_bytes, EMUL_index_pow, EMUL_data_pow, rs1, vs2, num_elem, 1) +} + +mapping clause assembly = VSUXSEGTYPE(nf, vm, vs2, rs1, width, vs3) + <-> "vsux" ^ nfields_string(nf) ^ "ei" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vs3) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ sep() ^ reg_name(vs2) ^ maybe_vmask(vm) + +/* ************************* Vector Store Indexed Ordered Normal & Segment (mop=0b11) ************************** */ +union clause ast = VSOXSEGTYPE : (bits(3), bits(1), regidx, regidx, vlewidth, regidx) + +mapping clause encdec = VSOXSEGTYPE(nf, vm, vs2, rs1, width, vs3) if haveVExt() + <-> nf @ 0b0 @ 0b11 @ vm @ vs2 @ rs1 @ encdec_vlewidth(width) @ vs3 @ 0b0100111 if haveVExt() + +function clause execute(VSOXSEGTYPE(nf, vm, vs2, rs1, width, vs3)) = { + let EEW_index_pow = vlewidth_pow(width); + let EEW_index_bytes = vlewidth_bytesnumber(width); + let EEW_data_pow = get_sew_pow(); + let EEW_data_bytes = get_sew_bytes(); + let EMUL_data_pow = get_lmul_pow(); + let EMUL_index_pow = EEW_index_pow - EEW_data_pow + EMUL_data_pow; + let num_elem = get_num_elem(EMUL_data_pow, EEW_data_bytes * 8); /* number of data and indices are the same */ + let nf_int = nfields_int(nf); + + if illegal_indexed_store(nf_int, EEW_index_bytes * 8, EMUL_index_pow, EMUL_data_pow) then { handle_illegal(); return RETIRE_FAIL }; + + process_vsxseg(nf_int, vm, vs3, EEW_index_bytes, EEW_data_bytes, EMUL_index_pow, EMUL_data_pow, rs1, vs2, num_elem, 3) +} + +mapping clause assembly = VSUXSEGTYPE(nf, vm, vs2, rs1, width, vs3) + <-> "vsox" ^ nfields_string(nf) ^ "ei" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vs3) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" ^ sep() ^ reg_name(vs2) ^ maybe_vmask(vm) + +/* ***************** Vector Load Unit-Stride Whole Register (vm=0b1, mop=0b00, lumop=0b01000) ****************** */ +union clause ast = VLRETYPE : (bits(3), regidx, vlewidth, regidx) + +mapping clause encdec = VLRETYPE(nf, rs1, width, vd) if haveVExt() + <-> nf @ 0b0 @ 0b00 @ 0b1 @ 0b01000 @ rs1 @ encdec_vlewidth(width) @ vd @ 0b0000111 if haveVExt() + +val process_vlre : forall 'f 'b 'n, ('f in {1, 2, 4, 8}) & ('b in {1, 2, 4, 8}) & ('n >= 0). (int('f), regidx, int('b), regidx, int('n)) -> Retired effect {escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vlre (nf, vd, load_width_bytes, rs1, elem_per_reg) = { + let width_type : word_width = bytes_wordwidth(load_width_bytes); + let start_element = get_start_element(); + if start_element >= nf * elem_per_reg then return RETIRE_SUCCESS; /* no elements are written if vstart >= evl */ + let elem_to_align : int = start_element % elem_per_reg; + cur_field : int = start_element / elem_per_reg; + cur_elem : int = start_element; + + if elem_to_align > 0 then { + foreach (i from elem_to_align to (elem_per_reg - 1)) { + vstart = to_bits(16, cur_elem); + let elem_offset = cur_elem * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Read(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_Load_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Read(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + match mem_read(Read(Data), paddr, load_width_bytes, false, false, false) { + MemValue(elem) => write_single_element(load_width_bytes * 8, i, vd + to_bits(5, cur_field), elem), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + }; + cur_elem = cur_elem + 1 + }; + cur_field = cur_field + 1 + }; + + foreach (j from cur_field to (nf - 1)) { + foreach (i from 0 to (elem_per_reg - 1)) { + vstart = to_bits(16, cur_elem); + let elem_offset = cur_elem * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Read(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_Load_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Read(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + match mem_read(Read(Data), paddr, load_width_bytes, false, false, false) { + MemValue(elem) => write_single_element(load_width_bytes * 8, i, vd + to_bits(5, j), elem), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + }; + cur_elem = cur_elem + 1 + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VLRETYPE(nf, rs1, width, vd)) = { + let load_width_bytes = vlewidth_bytesnumber(width); + let EEW = load_width_bytes * 8; + let VLEN = unsigned(vlenb) * 8; + let elem_per_reg : int = VLEN / EEW; + let nf_int = nfields_int(nf); + + assert(elem_per_reg >= 0); + if not(nf_int == 1 | nf_int == 2 | nf_int == 4 | nf_int == 8) then { handle_illegal(); return RETIRE_FAIL }; + + process_vlre(nf_int, vd, load_width_bytes, rs1, elem_per_reg) +} + +mapping clause assembly = VLRETYPE(nf, rs1, width, vd) + <-> "vl" ^ nfields_string(nf) ^ "re" ^ vlewidth_bitsnumberstr(width) ^ ".v" ^ spc() ^ vreg_name(vd) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" + +/* ***************** Vector Store Unit-Stride Whole Register (vm=0b1, mop=0b00, lumop=0b01000) ***************** */ +union clause ast = VSRETYPE : (bits(3), regidx, regidx) + +mapping clause encdec = VSRETYPE(nf, rs1, vs3) if haveVExt() + <-> nf @ 0b0 @ 0b00 @ 0b1 @ 0b01000 @ rs1 @ 0b000 @ vs3 @ 0b0100111 if haveVExt() + +val process_vsre : forall 'f 'b 'n, ('f in {1, 2, 4, 8}) & ('b in {1, 2, 4, 8}) & ('n >= 0). (int('f), int('b), regidx, regidx, int('n)) -> Retired effect {eamem, escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vsre (nf, load_width_bytes, rs1, vs3, elem_per_reg) = { + let width_type : word_width = BYTE; + let start_element = get_start_element(); + if start_element >= nf * elem_per_reg then return RETIRE_SUCCESS; /* no elements are written if vstart >= evl */ + let elem_to_align : int = start_element % elem_per_reg; + cur_field : int = start_element / elem_per_reg; + cur_elem : int = start_element; + + if elem_to_align > 0 then { + foreach (i from elem_to_align to (elem_per_reg - 1)) { + vstart = to_bits(16, cur_elem); + let elem_offset : int = cur_elem * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Write(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_SAMO_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Write(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + let eares : MemoryOpResult(unit) = mem_write_ea(paddr, load_width_bytes, false, false, false); + match (eares) { + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + MemValue(_) => { + let elem : bits('b * 8) = read_single_element(load_width_bytes * 8, i, vs3 + to_bits(5, cur_field)); + let res : MemoryOpResult(bool) = mem_write_value(paddr, load_width_bytes, elem, false, false, false); + match (res) { + MemValue(true) => (), + MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + }; + cur_elem = cur_elem + 1 + }; + cur_field = cur_field + 1 + }; + + foreach (j from cur_field to (nf - 1)) { + let vs3_val : vector('n, dec, bits('b * 8)) = read_vreg(elem_per_reg, load_width_bytes * 8, 0, vs3 + to_bits(5, j)); + foreach (i from 0 to (elem_per_reg - 1)) { + vstart = to_bits(16, cur_elem); + let elem_offset = cur_elem * load_width_bytes; + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), elem_offset), Write(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_SAMO_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Write(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + let eares : MemoryOpResult(unit) = mem_write_ea(paddr, load_width_bytes, false, false, false); + match (eares) { + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + MemValue(_) => { + let res : MemoryOpResult(bool) = mem_write_value(paddr, load_width_bytes, vs3_val[i], false, false, false); + match (res) { + MemValue(true) => (), + MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + }; + cur_elem = cur_elem + 1 + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VSRETYPE(nf, rs1, vs3)) = { + let load_width_bytes = 1; + let EEW = 8; + let VLEN = unsigned(vlenb) * 8; + let elem_per_reg : int = VLEN / EEW; + let nf_int = nfields_int(nf); + + assert(elem_per_reg >= 0); + if not(nf_int == 1 | nf_int == 2 | nf_int == 4 | nf_int == 8) then { handle_illegal(); return RETIRE_FAIL }; + + process_vsre(nf_int, load_width_bytes, rs1, vs3, elem_per_reg) +} + +mapping clause assembly = VSRETYPE(nf, rs1, vs3) + <-> "vs" ^ nfields_string(nf) ^ "r.v" ^ spc() ^ vreg_name(vs3) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" + +/* ************** Vector Mask Load/Store Unit-Stride (nf=0b000, mop=0b00, lumop or sumop=0b01011) ************** */ +union clause ast = VMTYPE : (regidx, regidx, vmlsop) + +mapping encdec_lsop : vmlsop <-> bits(7) = { + VLM <-> 0b0000111, + VSM <-> 0b0100111 +} + +mapping clause encdec = VMTYPE(rs1, vd_or_vs3, op) if haveVExt() + <-> 0b000 @ 0b0 @ 0b00 @ 0b1 @ 0b01011 @ rs1 @ 0b000 @ vd_or_vs3 @ encdec_lsop(op) if haveVExt() + +val process_vm : forall 'n 'l, ('n >= 0 & 'l >= 0). (regidx, regidx, int('n), int('l), vmlsop) -> Retired effect {eamem, escape, rmem, rmemt, rreg, undef, wmv, wmvt, wreg} +function process_vm(vd_or_vs3, rs1, num_elem, evl, op) = { + let width_type : word_width = BYTE; + let start_element = get_start_element(); + let vd_or_vs3_val : vector('n, dec, bits(8)) = read_vreg(num_elem, 8, 0, vd_or_vs3); + + foreach (i from start_element to (num_elem - 1)) { + if i < evl then { /* active elements */ + vstart = to_bits(16, i); + if op == VLM then { /* load */ + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), i), Read(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_Load_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Read(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + match mem_read(Read(Data), paddr, 1, false, false, false) { + MemValue(elem) => write_single_element(8, i, vd_or_vs3, elem), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } else if op == VSM then { /* store */ + match ext_data_get_addr(rs1, to_bits(sizeof(xlen), i), Write(Data), width_type) { + Ext_DataAddr_Error(e) => { ext_handle_data_check_error(e); return RETIRE_FAIL }, + Ext_DataAddr_OK(vaddr) => + if check_misaligned(vaddr, width_type) + then { handle_mem_exception(vaddr, E_SAMO_Addr_Align()); return RETIRE_FAIL } + else match translateAddr(vaddr, Write(Data)) { + TR_Failure(e, _) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + TR_Address(paddr, _) => { + let eares : MemoryOpResult(unit) = mem_write_ea(paddr, 1, false, false, false); + match (eares) { + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL }, + MemValue(_) => { + let res : MemoryOpResult(bool) = mem_write_value(paddr, 1, vd_or_vs3_val[i], false, false, false); + match (res) { + MemValue(true) => (), + MemValue(false) => internal_error(__FILE__, __LINE__, "store got false from mem_write_value"), + MemException(e) => { handle_mem_exception(vaddr, e); return RETIRE_FAIL } + } + } + } + } + } + } + } + } else { /* tail elements for mask load, always with agnostic policy */ + if op == VLM then { + write_single_element(8, i, vd_or_vs3, vd_or_vs3_val[i]) + /* TODO: configuration support for agnostic behavior */ + } + } + }; + + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(VMTYPE(rs1, vd_or_vs3, op)) = { + let EEW = 8; + let EMUL_pow = 0; + let vl_val = unsigned(vl); + let evl : int = if vl_val % 8 == 0 then vl_val / 8 else vl_val / 8 + 1; /* the effective vector length is evl=ceil(vl/8) */ + let num_elem = get_num_elem(EMUL_pow, EEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + assert(evl >= 0); + process_vm(vd_or_vs3, rs1, num_elem, evl, op) +} + +mapping vmtype_mnemonic : vmlsop <-> string = { + VLM <-> "vlm.v", + VSM <-> "vsm.v" +} + +mapping clause assembly = VMTYPE(rs1, vd_or_vs3, op) + <-> vmtype_mnemonic(op) ^ spc() ^ vreg_name(vd_or_vs3) ^ sep() ^ "(" ^ reg_name(rs1) ^ ")" diff --git a/model/riscv_insts_vext_red.sail b/model/riscv_insts_vext_red.sail new file mode 100755 index 000000000..6b756f13a --- /dev/null +++ b/model/riscv_insts_vext_red.sail @@ -0,0 +1,288 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements part of the vector extension. */ +/* Chapter 14: Vector Reduction Instructions */ +/* ******************************************************************************* */ + +/* ********************* OPIVV (Widening Integer Reduction) ********************** */ +union clause ast = RIVVTYPE : (rivvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_rivvfunct6 : rivvfunct6 <-> bits(6) = { + IVV_VWREDSUMU <-> 0b110000, + IVV_VWREDSUM <-> 0b110001 +} + +mapping clause encdec = RIVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_rivvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(RIVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + let num_elem_vs = get_num_elem(LMUL_pow, SEW); + let num_elem_vd = get_num_elem(0, SEW_widen); /* vd regardless of LMUL setting */ + + if illegal_reduction_widen(SEW_widen, LMUL_pow_widen) then { handle_illegal(); return RETIRE_FAIL }; + + if unsigned(vl) == 0 then return RETIRE_SUCCESS; /* if vl=0, no operation is performed */ + + let 'n = num_elem_vs; + let 'd = num_elem_vd; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem_vs, vm, 0b00000); + let vd_val : vector('d, dec, bits('o)) = read_vreg(num_elem_vd, SEW_widen, 0, vd); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem_vs, SEW, LMUL_pow, vs2); + let mask : vector('n, dec, bool) = init_masked_source(num_elem_vs, LMUL_pow, vm_val); + + sum : bits('o) = read_single_element(SEW_widen, 0, vs1); /* vs1 regardless of LMUL setting */ + foreach (i from 0 to (num_elem_vs - 1)) { + if mask[i] then { + let elem : bits('o) = match funct6 { + IVV_VWREDSUMU => to_bits(SEW_widen, unsigned(vs2_val[i])), + IVV_VWREDSUM => to_bits(SEW_widen, signed(vs2_val[i])) + }; + sum = sum + elem + } + }; + + write_single_element(SEW_widen, 0, vd, sum); + /* other elements in vd are treated as tail elements, currently remain unchanged */ + /* TODO: configuration support for agnostic behavior */ + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping rivvtype_mnemonic : rivvfunct6 <-> string = { + IVV_VWREDSUMU <-> "vwredsumu.vs", + IVV_VWREDSUM <-> "vwredsum.vs" +} + +mapping clause assembly = RIVVTYPE(funct6, vm, vs2, vs1, vd) + <-> rivvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ******************* OPMVV (Single-Width Integer Reduction) ******************** */ +union clause ast = RMVVTYPE : (rmvvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_rmvvfunct6 : rmvvfunct6 <-> bits(6) = { + MVV_VREDSUM <-> 0b000000, + MVV_VREDAND <-> 0b000001, + MVV_VREDOR <-> 0b000010, + MVV_VREDXOR <-> 0b000011, + MVV_VREDMINU <-> 0b000100, + MVV_VREDMIN <-> 0b000101, + MVV_VREDMAXU <-> 0b000110, + MVV_VREDMAX <-> 0b000111 +} + +mapping clause encdec = RMVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_rmvvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b010 @ vd @ 0b1010111 if haveVExt() + +function clause execute(RMVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem_vs = get_num_elem(LMUL_pow, SEW); + let num_elem_vd = get_num_elem(0, SEW); /* vd regardless of LMUL setting */ + + if illegal_reduction() then { handle_illegal(); return RETIRE_FAIL }; + + if unsigned(vl) == 0 then return RETIRE_SUCCESS; /* if vl=0, no operation is performed */ + + let 'n = num_elem_vs; + let 'd = num_elem_vd; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem_vs, vm, 0b00000); + let vd_val : vector('d, dec, bits('m)) = read_vreg(num_elem_vd, SEW, 0, vd); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem_vs, SEW, LMUL_pow, vs2); + let mask : vector('n, dec, bool) = init_masked_source(num_elem_vs, LMUL_pow, vm_val); + + sum : bits('m) = read_single_element(SEW, 0, vs1); /* vs1 regardless of LMUL setting */ + foreach (i from 0 to (num_elem_vs - 1)) { + if mask[i] then { + sum = match funct6 { + MVV_VREDSUM => sum + vs2_val[i], + MVV_VREDAND => sum & vs2_val[i], + MVV_VREDOR => sum | vs2_val[i], + MVV_VREDXOR => sum ^ vs2_val[i], + MVV_VREDMIN => to_bits(SEW, min(signed(vs2_val[i]), signed(sum))), + MVV_VREDMINU => to_bits(SEW, min(unsigned(vs2_val[i]), unsigned(sum))), + MVV_VREDMAX => to_bits(SEW, max(signed(vs2_val[i]), signed(sum))), + MVV_VREDMAXU => to_bits(SEW, max(unsigned(vs2_val[i]), unsigned(sum))) + } + } + }; + + write_single_element(SEW, 0, vd, sum); + /* other elements in vd are treated as tail elements, currently remain unchanged */ + /* TODO: configuration support for agnostic behavior */ + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping rmvvtype_mnemonic : rmvvfunct6 <-> string = { + MVV_VREDSUM <-> "vredsum.vs", + MVV_VREDAND <-> "vredand.vs", + MVV_VREDOR <-> "vredor.vs", + MVV_VREDXOR <-> "vredxor.vs", + MVV_VREDMINU <-> "vredminu.vs", + MVV_VREDMIN <-> "vredmin.vs", + MVV_VREDMAXU <-> "vredmaxu.vs", + MVV_VREDMAX <-> "vredmax.vs" +} + +mapping clause assembly = RMVVTYPE(funct6, vm, vs2, vs1, vd) + <-> rmvvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ********************** OPFVV (Floating-Point Reduction) *********************** */ +union clause ast = RFVVTYPE : (rfvvfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_rfvvfunct6 : rfvvfunct6 <-> bits(6) = { + FVV_VFREDOSUM <-> 0b000011, + FVV_VFREDUSUM <-> 0b000001, + FVV_VFREDMAX <-> 0b000111, + FVV_VFREDMIN <-> 0b000101, + FVV_VFWREDOSUM <-> 0b110011, + FVV_VFWREDUSUM <-> 0b110001 +} + +mapping clause encdec = RFVVTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_rfvvfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b001 @ vd @ 0b1010111 if haveVExt() + +val process_rfvv_single: forall 'n 'm 'p, 'n >= 0 & 'm in {8, 16, 32, 64}. (rfvvfunct6, bits(1), regidx, regidx, regidx, int('n), int('m), int('p)) -> Retired effect {escape, rreg, undef, wreg} +function process_rfvv_single(funct6, vm, vs2, vs1, vd, num_elem_vs, SEW, LMUL_pow) = { + let rm_3b = fcsr.FRM(); + let num_elem_vd = get_num_elem(0, SEW); /* vd regardless of LMUL setting */ + + if illegal_fp_reduction(SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + if unsigned(vl) == 0 then return RETIRE_SUCCESS; /* if vl=0, no operation is performed */ + + let 'n = num_elem_vs; + let 'd = num_elem_vd; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem_vs, vm, 0b00000); + let vd_val : vector('d, dec, bits('m)) = read_vreg(num_elem_vd, SEW, 0, vd); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem_vs, SEW, LMUL_pow, vs2); + let mask : vector('n, dec, bool) = init_masked_source(num_elem_vs, LMUL_pow, vm_val); + + sum : bits('m) = read_single_element(SEW, 0, vs1); /* vs1 regardless of LMUL setting */ + foreach (i from 0 to (num_elem_vs - 1)) { + if mask[i] then { + sum = match funct6 { + /* currently ordered/unordered sum reductions do the same operations */ + FVV_VFREDOSUM => fp_add(rm_3b, sum, vs2_val[i]), + FVV_VFREDUSUM => fp_add(rm_3b, sum, vs2_val[i]), + FVV_VFREDMAX => fp_max(sum, vs2_val[i]), + FVV_VFREDMIN => fp_min(sum, vs2_val[i]) + } + } + }; + + write_single_element(SEW, 0, vd, sum); + /* other elements in vd are treated as tail elements, currently remain unchanged */ + /* TODO: configuration support for agnostic behavior */ + vstart = zeros(); + RETIRE_SUCCESS +} + +val process_rfvv_widen: forall 'n 'm 'p, 'n >= 0 & 'm in {8, 16, 32, 64}. (rfvvfunct6, bits(1), regidx, regidx, regidx, int('n), int('m), int('p)) -> Retired effect {escape, rreg, undef, wreg} +function process_rfvv_widen(funct6, vm, vs2, vs1, vd, num_elem_vs, SEW, LMUL_pow) = { + let rm_3b = fcsr.FRM(); + let SEW_widen = SEW * 2; + let LMUL_pow_widen = LMUL_pow + 1; + let num_elem_vd = get_num_elem(0, SEW_widen); /* vd regardless of LMUL setting */ + + if illegal_fp_reduction_widen(SEW, rm_3b, SEW_widen, LMUL_pow_widen) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW >= 16 & SEW_widen <= 64); + + if unsigned(vl) == 0 then return RETIRE_SUCCESS; /* if vl=0, no operation is performed */ + + let 'n = num_elem_vs; + let 'd = num_elem_vd; + let 'm = SEW; + let 'o = SEW_widen; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem_vs, vm, 0b00000); + let vd_val : vector('d, dec, bits('o)) = read_vreg(num_elem_vd, SEW_widen, 0, vd); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem_vs, SEW, LMUL_pow, vs2); + let mask : vector('n, dec, bool) = init_masked_source(num_elem_vs, LMUL_pow, vm_val); + + sum : bits('o) = read_single_element(SEW_widen, 0, vs1); /* vs1 regardless of LMUL setting */ + foreach (i from 0 to (num_elem_vs - 1)) { + if mask[i] then { + /* currently ordered/unordered sum reductions do the same operations */ + sum = fp_add(rm_3b, sum, fp_widen(vs2_val[i])) + } + }; + + write_single_element(SEW_widen, 0, vd, sum); + /* other elements in vd are treated as tail elements, currently remain unchanged */ + /* TODO: configuration support for agnostic behavior */ + vstart = zeros(); + RETIRE_SUCCESS +} + +function clause execute(RFVVTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem_vs = get_num_elem(LMUL_pow, SEW); + + if funct6 == FVV_VFWREDOSUM | funct6 == FVV_VFWREDUSUM then + process_rfvv_widen(funct6, vm, vs2, vs1, vd, num_elem_vs, SEW, LMUL_pow) + else + process_rfvv_single(funct6, vm, vs2, vs1, vd, num_elem_vs, SEW, LMUL_pow) +} + +mapping rfvvtype_mnemonic : rfvvfunct6 <-> string = { + FVV_VFREDOSUM <-> "vfredosum.vs", + FVV_VFREDUSUM <-> "vfredusum.vs", + FVV_VFREDMAX <-> "vfredmax.vs", + FVV_VFREDMIN <-> "vfredmin.vs", + FVV_VFWREDOSUM <-> "vfwredosum.vs", + FVV_VFWREDUSUM <-> "vfwredusum.vs" +} + +mapping clause assembly = RFVVTYPE(funct6, vm, vs2, vs1, vd) + <-> rfvvtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) diff --git a/model/riscv_insts_vext_utils.sail b/model/riscv_insts_vext_utils.sail new file mode 100755 index 000000000..ba52f0927 --- /dev/null +++ b/model/riscv_insts_vext_utils.sail @@ -0,0 +1,1145 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Lei Chen */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements functions used by vector instructions. */ +/* ******************************************************************************* */ + +/* Vector mask mapping */ +mapping maybe_vmask : string <-> bits(1) = { + "" <-> 0b1, /* unmasked by default */ + sep() ^ "v0.t" <-> 0b0 +} + +/* Check for valid EEW and EMUL values in: + * 1. vector widening/narrowing instructions + * 2. vector load/store instructions + */ +val valid_eew_emul : (int, int) -> bool effect {rreg} +function valid_eew_emul(EEW, EMUL_pow) = { + let ELEN = int_power(2, get_elen_pow()); + EEW >= 8 & EEW <= ELEN & EMUL_pow >= -3 & EMUL_pow <= 3 +} + +/* Check for valid vtype setting + * 1. If the vill bit is set, then any attempt to execute a vector instruction that depends upon vtype will raise an illegal instruction exception. + * 2. vset{i}vl{i} and whole-register loads, stores, and moves do not depend upon vtype. + */ +val valid_vtype : unit -> bool effect {rreg} +function valid_vtype() = { + vtype.vill() == 0b0 +} + +/* Check for vstart value */ +val assert_vstart : int -> bool effect {rreg} +function assert_vstart(i) = { + unsigned(vstart) == i +} + +/* Check for valid floating-point operation types + * 1. Valid element width of floating-point numbers + * 2. Valid floating-point rounding mode + */ +val valid_fp_op : ({|8, 16, 32, 64|}, bits(3)) -> bool +function valid_fp_op(SEW, rm_3b) = { + /* 128-bit floating-point values will be supported in future extensions */ + let valid_sew = (SEW >= 16 & SEW <= 128); + let valid_rm = not(rm_3b == 0b101 | rm_3b == 0b110 | rm_3b == 0b111); + valid_sew & valid_rm +} + +/* Check for valid destination register when vector masking is enabled: + * The destination vector register group for a masked vector instruction + * cannot overlap the source mask register (v0), + * unless the destination vector register is being written with a mask value (e.g., compares) + * or the scalar result of a reduction. + */ +val valid_rd_mask : (regidx, bits(1)) -> bool +function valid_rd_mask(rd, vm) = { + vm != 0b0 | rd != 0b00000 +} + +/* Check for valid register overlap in vector widening/narrowing instructions: + * In a widening instruction, the overlap is valid only in the highest-numbered part + * of the destination register group, and the source EMUL is at least 1. + * In a narrowing instruction, the overlap is valid only in the lowest-numbered part + * of the source register group. + */ +val valid_reg_overlap : (regidx, regidx, int, int) -> bool +function valid_reg_overlap(rs, rd, EMUL_pow_rs, EMUL_pow_rd) = { + let rs_group = if EMUL_pow_rs > 0 then int_power(2, EMUL_pow_rs) else 1; + let rd_group = if EMUL_pow_rd > 0 then int_power(2, EMUL_pow_rd) else 1; + let rs_int = unsigned(rs); + let rd_int = unsigned(rd); + if EMUL_pow_rs < EMUL_pow_rd then { + (rs_int + rs_group <= rd_int) | (rs_int >= rd_int + rd_group) | + ((rs_int + rs_group == rd_int + rd_group) & (EMUL_pow_rs >= 0)) + } else if EMUL_pow_rs > EMUL_pow_rd then { + (rd_int <= rs_int) | (rd_int >= rs_int + rs_group) + } else true; +} + +/* Check for valid register grouping in vector segment load/store instructions: + * The EMUL of load vd or store vs3 times the number of fields per segment + * must not be larger than 8. (EMUL * NFIELDS <= 8) + */ +val valid_segment : (int, int) -> bool +function valid_segment(nf, EMUL_pow) = { + if EMUL_pow < 0 then nf / int_power(2, 0 - EMUL_pow) <= 8 + else nf * int_power(2, EMUL_pow) <= 8 +} + +/* ******************************************************************************* */ +/* The following functions summarize patterns of illegal instruction check. */ +/* ******************************************************************************* */ + +/* a. Normal check including vtype.vill field and vd/v0 overlap if vm = 0 */ +val illegal_normal : (regidx, bits(1)) -> bool +function illegal_normal(vd, vm) = { + not(valid_vtype()) | not(valid_rd_mask(vd, vm)) +} + +/* b. Masked check for instructions encoded with vm = 0 */ +val illegal_vd_masked : regidx -> bool +function illegal_vd_masked(vd) = { + not(valid_vtype()) | vd == 0b00000 +} + +/* c. Unmasked check for: + * 1. instructions encoded with vm = 1 + * 2. instructions with scalar rd: vcpop.m, vfirst.m + * 3. vd as mask register (eew = 1): + * vmadc.vvm/vxm/vim, vmsbc.vvm/vxm, mask logical, integer compare, vlm.v, vsm.v + */ +val illegal_vd_unmasked : unit -> bool +function illegal_vd_unmasked() = { + not(valid_vtype()) +} + +/* d. Variable width check for: + * 1. integer/fixed-point widening/narrowing instructions + * 2. vector integer extension: vzext, vsext + */ +val illegal_variable_width : (regidx, bits(1), int, int) -> bool +function illegal_variable_width(vd, vm, SEW_new, LMUL_pow_new) = { + not(valid_vtype()) | not(valid_rd_mask(vd, vm)) | not(valid_eew_emul(SEW_new, LMUL_pow_new)) +} + +/* e. Normal check for reduction instructions: + * The destination vector register can overlap the source operands, including the mask register. + * Vector reduction operations raise an illegal instruction exception if vstart is non-zero. + */ +val illegal_reduction : unit -> bool +function illegal_reduction() = { + not(valid_vtype()) | not(assert_vstart(0)) +} + +/* f. Variable width check for widening reduction instructions */ +val illegal_reduction_widen : (int, int) -> bool +function illegal_reduction_widen(SEW_widen, LMUL_pow_widen) = { + not(valid_vtype()) | not(assert_vstart(0)) | not(valid_eew_emul(SEW_widen, LMUL_pow_widen)) +} + +/* g. Normal check for floating-point instructions */ +val illegal_fp_normal : (regidx, bits(1), {|8, 16, 32, 64|}, bits(3)) -> bool +function illegal_fp_normal(vd, vm, SEW, rm_3b) = { + not(valid_vtype()) | not(valid_rd_mask(vd, vm)) | not(valid_fp_op(SEW, rm_3b)) +} + +/* h. Masked check for floating-point instructions encoded with vm = 0 */ +val illegal_fp_vd_masked : (regidx, {|8, 16, 32, 64|}, bits(3)) -> bool +function illegal_fp_vd_masked(vd, SEW, rm_3b) = { + not(valid_vtype()) | vd == 0b00000 | not(valid_fp_op(SEW, rm_3b)) +} + +/* i. Unmasked check for floating-point instructions encoded with vm = 1 */ +val illegal_fp_vd_unmasked : ({|8, 16, 32, 64|}, bits(3)) -> bool +function illegal_fp_vd_unmasked(SEW, rm_3b) = { + not(valid_vtype()) | not(valid_fp_op(SEW, rm_3b)) +} + +/* j. Variable width check for floating-point widening/narrowing instructions */ +val illegal_fp_variable_width : (regidx, bits(1), {|8, 16, 32, 64|}, bits(3), int, int) -> bool +function illegal_fp_variable_width(vd, vm, SEW, rm_3b, SEW_new, LMUL_pow_new) = { + not(valid_vtype()) | not(valid_rd_mask(vd, vm)) | not(valid_fp_op(SEW, rm_3b)) | + not(valid_eew_emul(SEW_new, LMUL_pow_new)) +} + +/* k. Normal check for floating-point reduction instructions */ +val illegal_fp_reduction : ({|8, 16, 32, 64|}, bits(3)) -> bool +function illegal_fp_reduction(SEW, rm_3b) = { + not(valid_vtype()) | not(assert_vstart(0)) | not(valid_fp_op(SEW, rm_3b)) +} + +/* l. Variable width check for floating-point widening reduction instructions */ +val illegal_fp_reduction_widen : ({|8, 16, 32, 64|}, bits(3), int, int) -> bool +function illegal_fp_reduction_widen(SEW, rm_3b, SEW_widen, LMUL_pow_widen) = { + not(valid_vtype()) | not(assert_vstart(0)) | not(valid_fp_op(SEW, rm_3b)) | + not(valid_eew_emul(SEW_widen, LMUL_pow_widen)) +} + +/* m. Non-indexed load instruction check */ +val illegal_load : (regidx, bits(1), int, int, int) -> bool +function illegal_load(vd, vm, nf, EEW, EMUL_pow) = { + not(valid_vtype()) | not(valid_rd_mask(vd, vm)) | + not(valid_eew_emul(EEW, EMUL_pow)) | not(valid_segment(nf, EMUL_pow)) +} + +/* n. Non-indexed store instruction check (with vs3 rather than vd) */ +val illegal_store : (int, int, int) -> bool +function illegal_store(nf, EEW, EMUL_pow) = { + not(valid_vtype()) | not(valid_eew_emul(EEW, EMUL_pow)) | not(valid_segment(nf, EMUL_pow)) +} + +/* o. Indexed load instruction check */ +val illegal_indexed_load : (regidx, bits(1), int, int, int, int) -> bool +function illegal_indexed_load(vd, vm, nf, EEW_index, EMUL_pow_index, EMUL_pow_data) = { + not(valid_vtype()) | not(valid_rd_mask(vd, vm)) | + not(valid_eew_emul(EEW_index, EMUL_pow_index)) | not(valid_segment(nf, EMUL_pow_data)) +} + +/* p. Indexed store instruction check (with vs3 rather than vd) */ +val illegal_indexed_store : (int, int, int, int) -> bool +function illegal_indexed_store(nf, EEW_index, EMUL_pow_index, EMUL_pow_data) = { + not(valid_vtype()) | not(valid_eew_emul(EEW_index, EMUL_pow_index)) | + not(valid_segment(nf, EMUL_pow_data)) +} + +/* Scalar register shaping */ +val get_scalar : forall 'm, 'm >= 8. (regidx, int('m)) -> bits('m) effect {escape, rreg} +function get_scalar(rs1, SEW) = { + if SEW <= sizeof(xlen) then { + /* Least significant SEW bits */ + X(rs1)[SEW - 1 .. 0] + } else { + /* Sign extend to SEW */ + sign_extend(SEW, X(rs1)) + } +} + +/* Get the starting element index from csr vtype */ +val get_start_element : unit -> nat effect {escape, rreg, wreg} +function get_start_element() = { + let start_element = unsigned(vstart); + let VLEN_pow = get_vlen_pow(); + let SEW_pow = get_sew_pow(); + /* The use of vstart values greater than the largest element + index for the current SEW setting is reserved. + It is recommended that implementations trap if vstart is out of bounds. + It is not required to trap, as a possible future use of upper vstart bits + is to store imprecise trap information. */ + if start_element > (2 ^ (3 + VLEN_pow - SEW_pow) - 1) then handle_illegal(); + start_element +} + +/* Get the ending element index from csr vl */ +val get_end_element : unit -> int effect {escape, rreg, wreg} +function get_end_element() = unsigned(vl) - 1 + +/* Mask handling; creates a pre-masked result vector for vstart, vl, vta/vma, and vm */ +/* vm should be baked into vm_val from doing read_vmask */ +/* tail masking when lmul < 1 is handled in write_vreg */ +/* Returns two vectors: + * vector1 is the result vector with values applied to masked elements + * vector2 is a "mask" vector that is true for an element if the corresponding element + * in the result vector should be updated by the calling instruction + */ +val init_masked_result : forall 'n 'm 'p, 'n >= 0. (int('n), int('m), int('p), vector('n, dec, bits('m)), vector('n, dec, bool)) -> (vector('n, dec, bits('m)), vector('n, dec, bool)) effect {escape, rreg, undef} +function init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vm_val) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + let tail_ag : agtype = get_vtype_vta(); + let mask_ag : agtype = get_vtype_vma(); + mask : vector('n, dec, bool) = undefined; + result : vector('n, dec, bits('m)) = undefined; + + /* Determine the actual number of elements when lmul < 1 */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / int_power(2, 0 - LMUL_pow); + assert(num_elem >= real_num_elem); + + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + /* Prestart elements defined by vstart */ + result[i] = vd_val[i]; + mask[i] = false + } else if i > end_element then { + /* Tail elements defined by vl */ + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + }; + mask[i] = false + } else if i >= real_num_elem then { + /* Tail elements defined by lmul < 1 */ + result[i] = match tail_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + }; + mask[i] = false + } else if not(vm_val[i]) then { + /* Inactive body elements defined by vm */ + result[i] = match mask_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + }; + mask[i] = false + } else { + /* Active body elements */ + mask[i] = true; + } + }; + + (result, mask) +} + +/* For instructions like vector reduction and vector store, + * masks on prestart, inactive and tail elements only affect the validation of source register elements + * (vs3 for store and vs2 for reduction). There's no destination register to be masked. + * In these cases, this function can be called to simply get the mask vector for vs (without the prepared vd result vector). + */ +val init_masked_source : forall 'n 'p, 'n >= 0. (int('n), int('p), vector('n, dec, bool)) -> vector('n, dec, bool) effect {escape, rreg, undef} +function init_masked_source(num_elem, LMUL_pow, vm_val) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + mask : vector('n, dec, bool) = undefined; + + /* Determine the actual number of elements when lmul < 1 */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / int_power(2, 0 - LMUL_pow); + assert(num_elem >= real_num_elem); + + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + /* Prestart elements defined by vstart */ + mask[i] = false + } else if i > end_element then { + /* Tail elements defined by vl */ + mask[i] = false + } else if i >= real_num_elem then { + /* Tail elements defined by lmul < 1 */ + mask[i] = false + } else if not(vm_val[i]) then { + /* Inactive body elements defined by vm */ + mask[i] = false + } else { + /* Active body elements */ + mask[i] = true; + } + }; + + mask +} + +/* Mask handling for carry functions that use masks as input/output */ +/* Only prestart and tail elements are masked in a mask value */ +val init_masked_result_carry : forall 'n 'm 'p, 'n >= 0. (int('n), int('m), int('p), vector('n, dec, bool)) -> (vector('n, dec, bool), vector('n, dec, bool)) effect {escape, rreg, undef} +function init_masked_result_carry(num_elem, SEW, LMUL_pow, vd_val) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + mask : vector('n, dec, bool) = undefined; + result : vector('n, dec, bool) = undefined; + + /* Determine the actual number of elements when lmul < 1 */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / int_power(2, 0 - LMUL_pow); + assert(num_elem >= real_num_elem); + + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + /* Prestart elements defined by vstart */ + result[i] = vd_val[i]; + mask[i] = false + } else if i > end_element then { + /* Tail elements defined by vl */ + /* Mask tail is always agnostic */ + result[i] = vd_val[i]; /* TODO: configuration support */ + mask[i] = false + } else if i >= real_num_elem then { + /* Tail elements defined by lmul < 1 */ + /* Mask tail is always agnostic */ + result[i] = vd_val[i]; /* TODO: configuration support */ + mask[i] = false + } else { + /* Active body elements */ + mask[i] = true + } + }; + + (result, mask) +} + +/* Mask handling for cmp functions that use masks as output */ +val init_masked_result_cmp : forall 'n 'm 'p, 'n >= 0. (int('n), int('m), int('p), vector('n, dec, bool), vector('n, dec, bool)) -> (vector('n, dec, bool), vector('n, dec, bool)) effect {escape, rreg, undef} +function init_masked_result_cmp(num_elem, SEW, LMUL_pow, vd_val, vm_val) = { + let start_element = get_start_element(); + let end_element = get_end_element(); + let mask_ag : agtype = get_vtype_vma(); + mask : vector('n, dec, bool) = undefined; + result : vector('n, dec, bool) = undefined; + + /* Determine the actual number of elements when lmul < 1 */ + let real_num_elem = if LMUL_pow >= 0 then num_elem else num_elem / int_power(2, 0 - LMUL_pow); + assert(num_elem >= real_num_elem); + + foreach (i from 0 to (num_elem - 1)) { + if i < start_element then { + /* Prestart elements defined by vstart */ + result[i] = vd_val[i]; + mask[i] = false + } else if i > end_element then { + /* Tail elements defined by vl */ + /* Mask tail is always agnostic */ + result[i] = vd_val[i]; /* TODO: configuration support */ + mask[i] = false + } else if i >= real_num_elem then { + /* Tail elements defined by lmul < 1 */ + /* Mask tail is always agnostic */ + result[i] = vd_val[i]; /* TODO: configuration support */ + mask[i] = false + } else if not(vm_val[i]) then { + /* Inactive body elements defined by vm */ + result[i] = match mask_ag { + UNDISTURBED => vd_val[i], + AGNOSTIC => vd_val[i] /* TODO: configuration support */ + }; + mask[i] = false + } else { + /* Active body elements */ + mask[i] = true + } + }; + + (result, mask) +} + +/* For vector load/store segment instructions: + * Read multiple register groups and concatenate them in parallel + * The whole segments with the same element index are combined together + */ +val read_vreg_seg : forall 'n 'm 'p 'q, 'n >= 0 & 'q >= 0. (int('n), int('m), int('p), int('q), regidx) -> vector('n, dec, bits('q * 'm)) effect {escape, rreg, undef} +function read_vreg_seg(num_elem, SEW, LMUL_pow, nf, vrid) = { + assert('q * 'm > 0); + let LMUL_reg : int = if LMUL_pow <= 0 then 1 else int_power(2, LMUL_pow); + vreg_list : vector('q, dec, vector('n, dec, bits('m))) = undefined; + result : vector('n, dec, bits('q * 'm)) = undefined; + foreach (j from 0 to (nf - 1)) { + vreg_list[j] = read_vreg(num_elem, SEW, LMUL_pow, vrid + to_bits(5, j * LMUL_reg)); + }; + foreach (i from 0 to (num_elem - 1)) { + result[i] = zeros('q * 'm); + foreach (j from 0 to (nf - 1)) { + result[i] = result[i] | (zero_extend(vreg_list[j][i]) << (j * 'm)) + } + }; + result +} + +/* Floating point canonical NaN for 16-bit, 32-bit and 64-bit types */ +val canonical_NaN : forall 'm, 'm in {16, 32, 64}. int('m) -> bits('m) +function canonical_NaN('m) = { + match 'm { + 16 => canonical_NaN_H(), + 32 => canonical_NaN_S(), + 64 => canonical_NaN_D() + } +} + +/* Floating point classification functions */ +val f_is_neg_inf : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_neg_inf(xf) = { + match 'm { + 16 => f_is_neg_inf_H(xf), + 32 => f_is_neg_inf_S(xf), + 64 => f_is_neg_inf_D(xf) + } +} + +val f_is_neg_norm : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_neg_norm(xf) = { + match 'm { + 16 => f_is_neg_norm_H(xf), + 32 => f_is_neg_norm_S(xf), + 64 => f_is_neg_norm_D(xf) + } +} + +val f_is_neg_subnorm : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_neg_subnorm(xf) = { + match 'm { + 16 => f_is_neg_subnorm_H(xf), + 32 => f_is_neg_subnorm_S(xf), + 64 => f_is_neg_subnorm_D(xf) + } +} + +val f_is_neg_zero : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_neg_zero(xf) = { + match 'm { + 16 => f_is_neg_zero_H(xf), + 32 => f_is_neg_zero_S(xf), + 64 => f_is_neg_zero_D(xf) + } +} + +val f_is_pos_zero : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_pos_zero(xf) = { + match 'm { + 16 => f_is_pos_zero_H(xf), + 32 => f_is_pos_zero_S(xf), + 64 => f_is_pos_zero_D(xf) + } +} + +val f_is_pos_subnorm : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_pos_subnorm(xf) = { + match 'm { + 16 => f_is_pos_subnorm_H(xf), + 32 => f_is_pos_subnorm_S(xf), + 64 => f_is_pos_subnorm_D(xf) + } +} + +val f_is_pos_norm : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_pos_norm(xf) = { + match 'm { + 16 => f_is_pos_norm_H(xf), + 32 => f_is_pos_norm_S(xf), + 64 => f_is_pos_norm_D(xf) + } +} + +val f_is_pos_inf : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_pos_inf(xf) = { + match 'm { + 16 => f_is_pos_inf_H(xf), + 32 => f_is_pos_inf_S(xf), + 64 => f_is_pos_inf_D(xf) + } +} + +val f_is_SNaN : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_SNaN(xf) = { + match 'm { + 16 => f_is_SNaN_H(xf), + 32 => f_is_SNaN_S(xf), + 64 => f_is_SNaN_D(xf) + } +} + +val f_is_QNaN : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_QNaN(xf) = { + match 'm { + 16 => f_is_QNaN_H(xf), + 32 => f_is_QNaN_S(xf), + 64 => f_is_QNaN_D(xf) + } +} + +val f_is_NaN : forall 'm, 'm in {16, 32, 64}. bits('m) -> bool +function f_is_NaN(xf) = { + match 'm { + 16 => f_is_NaN_H(xf), + 32 => f_is_NaN_S(xf), + 64 => f_is_NaN_D(xf) + } +} + +/* Scalar register shaping for floating point operations */ +val get_scalar_fp : forall 'n, 'n in {16, 32, 64}. (regidx, int('n)) -> bits('n) effect {escape, rreg} +function get_scalar_fp(rs1, SEW) = { + assert(sizeof(flen) >= SEW, "invalid vector floating-point type width: FLEN < SEW"); + match SEW { + 16 => F_H(rs1), + 32 => F_S(rs1), + 64 => F_D(rs1) + } +} + +/* Shift amounts */ +val get_shift_amount : forall 'n 'm, 0 <= 'n & 'm in {8, 16, 32, 64}. (bits('n), int('m)) -> nat effect {escape} +function get_shift_amount(bit_val, SEW) = { + let lowlog2bits = log2(SEW); + assert(0 < lowlog2bits & lowlog2bits < 'n); + unsigned(bit_val[lowlog2bits - 1 .. 0]); +} + +/* Fixed point rounding increment */ +val get_fixed_rounding_incr : forall ('m 'n : Int), ('m > 0 & 'n >= 0). (bits('m), int('n)) -> bits(1) effect {rreg, undef} +function get_fixed_rounding_incr(vec_elem, shift_amount) = { + if shift_amount == 0 then 0b0 + else { + let rounding_mode = vxrm[1 .. 0]; + match rounding_mode { + 0b00 => slice(vec_elem, shift_amount - 1, 1), + 0b01 => bool_to_bits( + (slice(vec_elem, shift_amount - 1, 1) == 0b1) & (slice(vec_elem, 0, shift_amount - 1) != zeros() | slice(vec_elem, shift_amount, 1) == 0b1)), + 0b10 => 0b0, + 0b11 => bool_to_bits( + not(slice(vec_elem, shift_amount, 1) == 0b1) & (slice(vec_elem, 0, shift_amount) != zeros())) + } + } +} + +/* Fixed point unsigned saturation */ +val unsigned_saturation : forall ('m 'n: Int), ('n >= 'm > 1). (int('m), bits('n)) -> bits('m) effect {escape, rreg, undef, wreg} +function unsigned_saturation(len, elem) = { + if unsigned(elem) > unsigned(ones('m)) then { + vxsat = 0b1; + ones('m) + } else { + vxsat = 0b0; + elem['m - 1 .. 0] + } +} + +/* Fixed point signed saturation */ +val signed_saturation : forall ('m 'n: Int), ('n >= 'm > 1). (int('m), bits('n)) -> bits('m) effect {escape, rreg, undef, wreg} +function signed_saturation(len, elem) = { + if signed(elem) > signed(0b0 @ ones('m - 1)) then { + vxsat = 0b1; + 0b0 @ ones('m - 1) + } else if signed(elem) < signed(0b1 @ zeros('m - 1)) then { + vxsat = 0b1; + 0b1 @ zeros('m - 1) + } else { + vxsat = 0b0; + elem['m - 1 .. 0] + }; +} + +/* Get the floating point rounding mode from csr fcsr */ +val get_fp_rounding_mode : unit -> rounding_mode effect {rreg} +function get_fp_rounding_mode() = encdec_rounding_mode(fcsr.FRM()) + +/* Negate a floating point number */ +val negate_fp : forall 'm, 'm in {16, 32, 64}. bits('m) -> bits('m) +function negate_fp(xf) = { + match 'm { + 16 => negate_H(xf), + 32 => negate_S(xf), + 64 => negate_D(xf) + } +} + +/* Floating point functions using softfloat interface */ +val fp_add: forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_add(rm_3b, op1, op2) = { + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16Add(rm_3b, op1, op2), + 32 => riscv_f32Add(rm_3b, op1, op2), + 64 => riscv_f64Add(rm_3b, op1, op2) + }; + write_fflags(fflags); + result_val +} + +val fp_sub: forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_sub(rm_3b, op1, op2) = { + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16Sub(rm_3b, op1, op2), + 32 => riscv_f32Sub(rm_3b, op1, op2), + 64 => riscv_f64Sub(rm_3b, op1, op2) + }; + write_fflags(fflags); + result_val +} + +val fp_min : forall 'm, 'm in {16, 32, 64}. (bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_min(op1, op2) = { + let (fflags, op1_lt_op2) : (bits_fflags, bool) = match 'm { + 16 => riscv_f16Lt_quiet(op1, op2), + 32 => riscv_f32Lt_quiet(op1, op2), + 64 => riscv_f64Lt_quiet(op1, op2) + }; + + let result_val = if (f_is_NaN(op1) & f_is_NaN(op2)) then canonical_NaN('m) + else if f_is_NaN(op1) then op2 + else if f_is_NaN(op2) then op1 + else if (f_is_neg_zero(op1) & f_is_pos_zero(op2)) then op1 + else if (f_is_neg_zero(op2) & f_is_pos_zero(op1)) then op2 + else if op1_lt_op2 then op1 + else op2; + write_fflags(fflags); + result_val +} + +val fp_max : forall 'm, 'm in {16, 32, 64}. (bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_max(op1, op2) = { + let (fflags, op1_lt_op2) : (bits_fflags, bool) = match 'm { + 16 => riscv_f16Lt_quiet(op1, op2), + 32 => riscv_f32Lt_quiet(op1, op2), + 64 => riscv_f64Lt_quiet(op1, op2) + }; + + let result_val = if (f_is_NaN(op1) & f_is_NaN(op2)) then canonical_NaN('m) + else if f_is_NaN(op1) then op2 + else if f_is_NaN(op2) then op1 + else if (f_is_neg_zero(op1) & f_is_pos_zero(op2)) then op2 + else if (f_is_neg_zero(op2) & f_is_pos_zero(op1)) then op1 + else if op1_lt_op2 then op2 + else op1; + write_fflags(fflags); + result_val +} + +val fp_eq : forall 'm, 'm in {16, 32, 64}. (bits('m), bits('m)) -> bool effect {escape, rreg, undef, wreg} +function fp_eq(op1, op2) = { + let (fflags, result_val) : (bits_fflags, bool) = match 'm { + 16 => riscv_f16Eq(op1, op2), + 32 => riscv_f32Eq(op1, op2), + 64 => riscv_f64Eq(op1, op2) + }; + write_fflags(fflags); + result_val +} + +val fp_gt : forall 'm, 'm in {16, 32, 64}. (bits('m), bits('m)) -> bool effect {escape, rreg, undef, wreg} +function fp_gt(op1, op2) = { + let (fflags, temp_val) : (bits_fflags, bool) = match 'm { + 16 => riscv_f16Le(op1, op2), + 32 => riscv_f32Le(op1, op2), + 64 => riscv_f64Le(op1, op2) + }; + let result_val = (if fflags == 0b10000 then false else not(temp_val)); + write_fflags(fflags); + result_val +} + +val fp_ge : forall 'm, 'm in {16, 32, 64}. (bits('m), bits('m)) -> bool effect {escape, rreg, undef, wreg} +function fp_ge(op1, op2) = { + let (fflags, temp_val) : (bits_fflags, bool) = match 'm { + 16 => riscv_f16Lt(op1, op2), + 32 => riscv_f32Lt(op1, op2), + 64 => riscv_f64Lt(op1, op2) + }; + let result_val = (if fflags == 0b10000 then false else not(temp_val)); + write_fflags(fflags); + result_val +} + +val fp_lt : forall 'm, 'm in {16, 32, 64}. (bits('m), bits('m)) -> bool effect {escape, rreg, undef, wreg} +function fp_lt(op1, op2) = { + let (fflags, result_val) : (bits_fflags, bool) = match 'm { + 16 => riscv_f16Lt(op1, op2), + 32 => riscv_f32Lt(op1, op2), + 64 => riscv_f64Lt(op1, op2) + }; + write_fflags(fflags); + result_val +} + +val fp_le : forall 'm, 'm in {16, 32, 64}. (bits('m), bits('m)) -> bool effect {escape, rreg, undef, wreg} +function fp_le(op1, op2) = { + let (fflags, result_val) : (bits_fflags, bool) = match 'm { + 16 => riscv_f16Le(op1, op2), + 32 => riscv_f32Le(op1, op2), + 64 => riscv_f64Le(op1, op2) + }; + write_fflags(fflags); + result_val +} + +val fp_mul : forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_mul(rm_3b, op1, op2) = { + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16Mul(rm_3b, op1, op2), + 32 => riscv_f32Mul(rm_3b, op1, op2), + 64 => riscv_f64Mul(rm_3b, op1, op2) + }; + write_fflags(fflags); + result_val +} + +val fp_div : forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_div(rm_3b, op1, op2) = { + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16Div(rm_3b, op1, op2), + 32 => riscv_f32Div(rm_3b, op1, op2), + 64 => riscv_f64Div(rm_3b, op1, op2) + }; + write_fflags(fflags); + result_val +} + +val fp_muladd : forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_muladd(rm_3b, op1, op2, opadd) = { + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16MulAdd(rm_3b, op1, op2, opadd), + 32 => riscv_f32MulAdd(rm_3b, op1, op2, opadd), + 64 => riscv_f64MulAdd(rm_3b, op1, op2, opadd) + }; + write_fflags(fflags); + result_val +} + +val fp_nmuladd : forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_nmuladd(rm_3b, op1, op2, opadd) = { + let op1 = negate_fp(op1); + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16MulAdd(rm_3b, op1, op2, opadd), + 32 => riscv_f32MulAdd(rm_3b, op1, op2, opadd), + 64 => riscv_f64MulAdd(rm_3b, op1, op2, opadd) + }; + write_fflags(fflags); + result_val +} + +val fp_mulsub : forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_mulsub(rm_3b, op1, op2, opsub) = { + let opsub = negate_fp(opsub); + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16MulAdd(rm_3b, op1, op2, opsub), + 32 => riscv_f32MulAdd(rm_3b, op1, op2, opsub), + 64 => riscv_f64MulAdd(rm_3b, op1, op2, opsub) + }; + write_fflags(fflags); + result_val +} + +val fp_nmulsub : forall 'm, 'm in {16, 32, 64}. (bits(3), bits('m), bits('m), bits('m)) -> bits('m) effect {escape, rreg, undef, wreg} +function fp_nmulsub(rm_3b, op1, op2, opsub) = { + let opsub = negate_fp(opsub); + let op1 = negate_fp(op1); + let (fflags, result_val) : (bits_fflags, bits('m)) = match 'm { + 16 => riscv_f16MulAdd(rm_3b, op1, op2, opsub), + 32 => riscv_f32MulAdd(rm_3b, op1, op2, opsub), + 64 => riscv_f64MulAdd(rm_3b, op1, op2, opsub) + }; + write_fflags(fflags); + result_val +} + +val fp_class : forall 'm, 'm in {16, 32, 64}. bits('m) -> bits('m) +function fp_class(xf) = { + let result_val_10b : bits(10) = + if f_is_neg_inf (xf) then 0b_00_0000_0001 + else if f_is_neg_norm (xf) then 0b_00_0000_0010 + else if f_is_neg_subnorm (xf) then 0b_00_0000_0100 + else if f_is_neg_zero (xf) then 0b_00_0000_1000 + else if f_is_pos_zero (xf) then 0b_00_0001_0000 + else if f_is_pos_subnorm (xf) then 0b_00_0010_0000 + else if f_is_pos_norm (xf) then 0b_00_0100_0000 + else if f_is_pos_inf (xf) then 0b_00_1000_0000 + else if f_is_SNaN (xf) then 0b_01_0000_0000 + else if f_is_QNaN (xf) then 0b_10_0000_0000 + else zeros(); + + zero_extend(result_val_10b) +} + +val fp_widen : forall 'm, 'm in {16, 32}. bits('m) -> bits('m * 2) effect {escape, rreg, undef, wreg} +function fp_widen(nval) = { + let rm_3b = fcsr.FRM(); + let (fflags, wval) : (bits_fflags, bits('m * 2)) = match 'm { + 16 => riscv_f16ToF32(rm_3b, nval), + 32 => riscv_f32ToF64(rm_3b, nval) + }; + accrue_fflags(fflags); + wval +} + +/* Floating point functions without softfloat support */ +val riscv_f16ToI16 : (bits_rm, bits_H) -> (bits_fflags, bits(16)) effect {rreg} +function riscv_f16ToI16 (rm, v) = { + let (_, sig32) = riscv_f16ToI32(rm, v); + if signed(sig32) > signed(0b0 @ ones(15)) then (nvFlag(), 0b0 @ ones(15)) + else if signed(sig32) < signed(0b1 @ zeros(15)) then (nvFlag(), 0b1 @ zeros(15)) + else (zeros(5), sig32[15 .. 0]); +} + +val riscv_f16ToI8 : (bits_rm, bits_H) -> (bits_fflags, bits(8)) effect {rreg} +function riscv_f16ToI8 (rm, v) = { + let (_, sig32) = riscv_f16ToI32(rm, v); + if signed(sig32) > signed(0b0 @ ones(7)) then (nvFlag(), 0b0 @ ones(7)) + else if signed(sig32) < signed(0b1 @ zeros(7)) then (nvFlag(), 0b1 @ zeros(7)) + else (zeros(5), sig32[7 .. 0]); +} + +val riscv_f32ToI16 : (bits_rm, bits_S) -> (bits_fflags, bits(16)) effect {rreg} +function riscv_f32ToI16 (rm, v) = { + let (_, sig32) = riscv_f32ToI32(rm, v); + if signed(sig32) > signed(0b0 @ ones(15)) then (nvFlag(), 0b0 @ ones(15)) + else if signed(sig32) < signed(0b1 @ zeros(15)) then (nvFlag(), 0b1 @ zeros(15)) + else (zeros(5), sig32[15 .. 0]); +} + +val riscv_f16ToUi16 : (bits_rm, bits_H) -> (bits_fflags, bits(16)) effect {rreg} +function riscv_f16ToUi16 (rm, v) = { + let (_, sig32) = riscv_f16ToUi32(rm, v); + if unsigned(sig32) > unsigned(ones(16)) then (nvFlag(), ones(16)) + else (zeros(5), sig32[15 .. 0]); +} + +val riscv_f16ToUi8 : (bits_rm, bits_H) -> (bits_fflags, bits(8)) effect {rreg} +function riscv_f16ToUi8 (rm, v) = { + let (_, sig32) = riscv_f16ToUi32(rm, v); + if unsigned(sig32) > unsigned(ones(8)) then (nvFlag(), ones(8)) + else (zeros(5), sig32[7 .. 0]); +} + +val riscv_f32ToUi16 : (bits_rm, bits_S) -> (bits_fflags, bits(16)) effect {rreg} +function riscv_f32ToUi16 (rm, v) = { + let (_, sig32) = riscv_f32ToUi32(rm, v); + if unsigned(sig32) > unsigned(ones(16)) then (nvFlag(), ones(16)) + else (zeros(5), sig32[15 .. 0]); +} + +val count_leadingzeros : (bits(64), int) -> int +function count_leadingzeros (sig, len) = { + idx : int = -1; + assert(len == 10 | len == 23 | len == 52); + foreach (i from 0 to (len - 1)) { + if sig[i] == bitone then idx = i; + }; + len - idx - 1 +} + +val rsqrt7 : forall 'm, 'm in {16, 32, 64}. (bits('m), bool) -> bits_D +function rsqrt7 (v, sub) = { + let (sig, exp, sign, e, s) : (bits(64), bits(64), bits(1), nat, nat) = match 'm { + 16 => (zero_extend(64, v[9 .. 0]), zero_extend(64, v[14 .. 10]), [v[15]], 5, 10), + 32 => (zero_extend(64, v[22 .. 0]), zero_extend(64, v[30 .. 23]), [v[31]], 8, 23), + 64 => (zero_extend(64, v[51 .. 0]), zero_extend(64, v[62 .. 52]), [v[63]], 11, 52) + }; + assert(s == 10 & e == 5 | s == 23 & e == 8 | s == 52 & e == 11); + let table : vector(128, dec, int) = [ + 52, 51, 50, 48, 47, 46, 44, 43, + 42, 41, 40, 39, 38, 36, 35, 34, + 33, 32, 31, 30, 30, 29, 28, 27, + 26, 25, 24, 23, 23, 22, 21, 20, + 19, 19, 18, 17, 16, 16, 15, 14, + 14, 13, 12, 12, 11, 10, 10, 9, + 9, 8, 7, 7, 6, 6, 5, 4, + 4, 3, 3, 2, 2, 1, 1, 0, + 127, 125, 123, 121, 119, 118, 116, 114, + 113, 111, 109, 108, 106, 105, 103, 102, + 100, 99, 97, 96, 95, 93, 92, 91, + 90, 88, 87, 86, 85, 84, 83, 82, + 80, 79, 78, 77, 76, 75, 74, 73, + 72, 71, 70, 70, 69, 68, 67, 66, + 65, 64, 63, 63, 62, 61, 60, 59, + 59, 58, 57, 56, 56, 55, 54, 53]; + + let (normalized_exp, normalized_sig) = + if sub then { + let nr_leadingzeros = count_leadingzeros(sig, s); + assert(nr_leadingzeros >= 0); + (to_bits(64, (0 - nr_leadingzeros)), zero_extend(64, sig[(s - 1) .. 0] << (1 + nr_leadingzeros))) + } else { + (exp, sig) + }; + + let idx : nat = match 'm { + 16 => unsigned([normalized_exp[0]] @ normalized_sig[9 .. 4]), + 32 => unsigned([normalized_exp[0]] @ normalized_sig[22 .. 17]), + 64 => unsigned([normalized_exp[0]] @ normalized_sig[51 .. 46]) + }; + assert(idx >= 0 & idx < 128); + let out_sig = to_bits(s, table[(127 - idx)]) << (s - 7); + let out_exp = to_bits(e, (3 * (2^(e - 1) - 1) - 1 - signed(normalized_exp)) / 2); + zero_extend(64, sign @ out_exp @ out_sig) +} + +val riscv_f16Rsqrte7 : (bits_rm, bits_H) -> (bits_fflags, bits_H) effect {rreg} +function riscv_f16Rsqrte7 (rm, v) = { + let class = fp_class(v); + let (fflags, result) : (bits_fflags, bits_H)= match class { + 0x0001 => (nvFlag(), 0x7e00), + 0x0002 => (nvFlag(), 0x7e00), + 0x0004 => (nvFlag(), 0x7e00), + 0x0100 => (nvFlag(), 0x7e00), + 0x0200 => (zeros(5), 0x7e00), + 0x0008 => (dzFlag(), 0xfc00), + 0x0010 => (dzFlag(), 0x7c00), + 0x0080 => (zeros(5), 0x0000), + 0x0020 => (zeros(5), rsqrt7(v, true)[15 .. 0]), + _ => (zeros(5), rsqrt7(v, false)[15 .. 0]) + }; + (fflags, result) +} + +val riscv_f32Rsqrte7 : (bits_rm, bits_S) -> (bits_fflags, bits_S) effect {rreg} +function riscv_f32Rsqrte7 (rm, v) = { + let class = fp_class(v); + let (fflags, result) : (bits_fflags, bits_S)= match class[15 .. 0] { + 0x0001 => (nvFlag(), 0x7fc00000), + 0x0002 => (nvFlag(), 0x7fc00000), + 0x0004 => (nvFlag(), 0x7fc00000), + 0x0100 => (nvFlag(), 0x7fc00000), + 0x0200 => (zeros(5), 0x7fc00000), + 0x0008 => (dzFlag(), 0xff800000), + 0x0010 => (dzFlag(), 0x7f800000), + 0x0080 => (zeros(5), 0x00000000), + 0x0020 => (zeros(5), rsqrt7(v, true)[31 .. 0]), + _ => (zeros(5), rsqrt7(v, false)[31 .. 0]) + }; + (fflags, result) +} + +val riscv_f64Rsqrte7 : (bits_rm, bits_D) -> (bits_fflags, bits_D) effect {rreg} +function riscv_f64Rsqrte7 (rm, v) = { + let class = fp_class(v); + let (fflags, result) : (bits_fflags, bits_D)= match class[15 .. 0] { + 0x0001 => (nvFlag(), 0x7ff8000000000000), + 0x0002 => (nvFlag(), 0x7ff8000000000000), + 0x0004 => (nvFlag(), 0x7ff8000000000000), + 0x0100 => (nvFlag(), 0x7ff8000000000000), + 0x0200 => (zeros(5), 0x7ff8000000000000), + 0x0008 => (dzFlag(), 0xfff0000000000000), + 0x0010 => (dzFlag(), 0x7ff0000000000000), + 0x0080 => (zeros(5), zeros(64)), + 0x0020 => (zeros(5), rsqrt7(v, true)[63 .. 0]), + _ => (zeros(5), rsqrt7(v, false)[63 .. 0]) + }; + (fflags, result) +} + +val recip7 : forall 'm, 'm in {16, 32, 64}. (bits('m), bits(3), bool) -> (bool, bits_D) +function recip7 (v, rm_3b, sub) = { + let (sig, exp, sign, e, s) : (bits(64), bits(64), bits(1), nat, nat) = match 'm { + 16 => (zero_extend(64, v[9 .. 0]), zero_extend(64, v[14 .. 10]), [v[15]], 5, 10), + 32 => (zero_extend(64, v[22 .. 0]), zero_extend(64, v[30 .. 23]), [v[31]], 8, 23), + 64 => (zero_extend(64, v[51 .. 0]), zero_extend(64, v[62 .. 52]), [v[63]], 11, 52) + }; + assert(s == 10 & e == 5 | s == 23 & e == 8 | s == 52 & e == 11); + let table : vector(128, dec, int) = [ + 127, 125, 123, 121, 119, 117, 116, 114, + 112, 110, 109, 107, 105, 104, 102, 100, + 99, 97, 96, 94, 93, 91, 90, 88, + 87, 85, 84, 83, 81, 80, 79, 77, + 76, 75, 74, 72, 71, 70, 69, 68, + 66, 65, 64, 63, 62, 61, 60, 59, + 58, 57, 56, 55, 54, 53, 52, 51, + 50, 49, 48, 47, 46, 45, 44, 43, + 42, 41, 40, 40, 39, 38, 37, 36, + 35, 35, 34, 33, 32, 31, 31, 30, + 29, 28, 28, 27, 26, 25, 25, 24, + 23, 23, 22, 21, 21, 20, 19, 19, + 18, 17, 17, 16, 15, 15, 14, 14, + 13, 12, 12, 11, 11, 10, 9, 9, + 8, 8, 7, 7, 6, 5, 5, 4, + 4, 3, 3, 2, 2, 1, 1, 0]; + + let nr_leadingzeros = count_leadingzeros(sig, s); + assert(nr_leadingzeros >= 0); + let (normalized_exp, normalized_sig) = + if sub then { + (to_bits(64, (0 - nr_leadingzeros)), zero_extend(64, sig[(s - 1) .. 0] << (1 + nr_leadingzeros))) + } else { + (exp, sig) + }; + + let idx : nat = match 'm { + 16 => unsigned(normalized_sig[9 .. 3]), + 32 => unsigned(normalized_sig[22 .. 16]), + 64 => unsigned(normalized_sig[51 .. 45]) + }; + assert(idx >= 0 & idx < 128); + let mid_exp = to_bits(e, 2 * (2^(e - 1) - 1) - 1 - signed(normalized_exp)); + let mid_sig = to_bits(s, table[(127 - idx)]) << (s - 7); + + let (out_exp, out_sig)= + if mid_exp == zeros(e) then { + (mid_exp, mid_sig >> 1 | 0b1 @ zeros(s - 1)) + } else if mid_exp == ones(e) then { + (zeros(e), mid_sig >> 2 | 0b01 @ zeros(s - 2)) + } else (mid_exp, mid_sig); + + if sub & nr_leadingzeros > 1 then { + if (rm_3b == 0b001 | rm_3b == 0b010 & sign == 0b0 | rm_3b == 0b011 & sign == 0b1) then { + (true, zero_extend(64, sign @ ones(e - 1) @ 0b0 @ ones(s))) + } + else (true, zero_extend(64, sign @ ones(e) @ zeros(s))) + } else (false, zero_extend(64, sign @ out_exp @ out_sig)) +} + +val riscv_f16Recip7 : (bits_rm, bits_H) -> (bits_fflags, bits_H) effect {rreg} +function riscv_f16Recip7 (rm, v) = { + let class = fp_class(v); + let (round_abnormal_true, res_true) = recip7(v, rm, true); + let (round_abnormal_false, res_false) = recip7(v, rm, false); + let (fflags, result) : (bits_fflags, bits_H) = match class { + 0x0001 => (zeros(5), 0x8000), + 0x0080 => (zeros(5), 0x0000), + 0x0008 => (dzFlag(), 0xfc00), + 0x0010 => (dzFlag(), 0x7c00), + 0x0100 => (nvFlag(), 0x7e00), + 0x0200 => (zeros(5), 0x7e00), + 0x0004 => if round_abnormal_true then (nxFlag() | ofFlag(), res_true[15 .. 0]) else (zeros(5), res_true[15 .. 0]), + 0x0020 => if round_abnormal_true then (nxFlag() | ofFlag(), res_true[15 .. 0]) else (zeros(5), res_true[15 .. 0]), + _ => if round_abnormal_false then (nxFlag() | ofFlag(), res_false[15 .. 0]) else (zeros(5), res_false[15 .. 0]) + }; + (fflags, result) +} + +val riscv_f32Recip7 : (bits_rm, bits_S) -> (bits_fflags, bits_S) effect {rreg} +function riscv_f32Recip7 (rm, v) = { + let class = fp_class(v); + let (round_abnormal_true, res_true) = recip7(v, rm, true); + let (round_abnormal_false, res_false) = recip7(v, rm, false); + let (fflags, result) : (bits_fflags, bits_S) = match class[15 .. 0] { + 0x0001 => (zeros(5), 0x80000000), + 0x0080 => (zeros(5), 0x00000000), + 0x0008 => (dzFlag(), 0xff800000), + 0x0010 => (dzFlag(), 0x7f800000), + 0x0100 => (nvFlag(), 0x7fc00000), + 0x0200 => (zeros(5), 0x7fc00000), + 0x0004 => if round_abnormal_true then (nxFlag() | ofFlag(), res_true[31 .. 0]) else (zeros(5), res_true[31 .. 0]), + 0x0020 => if round_abnormal_true then (nxFlag() | ofFlag(), res_true[31 .. 0]) else (zeros(5), res_true[31 .. 0]), + _ => if round_abnormal_false then (nxFlag() | ofFlag(), res_false[31 .. 0]) else (zeros(5), res_false[31 .. 0]) + }; + (fflags, result) +} + +val riscv_f64Recip7 : (bits_rm, bits_D) -> (bits_fflags, bits_D) effect {rreg} +function riscv_f64Recip7 (rm, v) = { + let class = fp_class(v); + let (round_abnormal_true, res_true) = recip7(v, rm, true); + let (round_abnormal_false, res_false) = recip7(v, rm, false); + let (fflags, result) : (bits_fflags, bits_D) = match class[15 .. 0] { + 0x0001 => (zeros(5), 0x8000000000000000), + 0x0080 => (zeros(5), 0x0000000000000000), + 0x0008 => (dzFlag(), 0xfff0000000000000), + 0x0010 => (dzFlag(), 0x7ff0000000000000), + 0x0100 => (nvFlag(), 0x7ff8000000000000), + 0x0200 => (zeros(5), 0x7ff8000000000000), + 0x0004 => if round_abnormal_true then (nxFlag() | ofFlag(), res_true[63 .. 0]) else (zeros(5), res_true[63 .. 0]), + 0x0020 => if round_abnormal_true then (nxFlag() | ofFlag(), res_true[63 .. 0]) else (zeros(5), res_true[63 .. 0]), + _ => if round_abnormal_false then (nxFlag() | ofFlag(), res_false[63 .. 0]) else (zeros(5), res_false[63 .. 0]) + }; + (fflags, result) +} diff --git a/model/riscv_insts_vext_vm.sail b/model/riscv_insts_vext_vm.sail new file mode 100755 index 000000000..b75b0799c --- /dev/null +++ b/model/riscv_insts_vext_vm.sail @@ -0,0 +1,883 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements part of the vector extension. */ +/* Mask instructions from Chap 11 (integer arithmetic) and 13 (floating-point) */ +/* ******************************************************************************* */ + +/* ******************************* OPIVV (VVMTYPE) ******************************* */ +/* VVM instructions' destination is a mask register (e.g. carry out) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VVMTYPE : (vvmfunct6, regidx, regidx, regidx) + +mapping encdec_vvmfunct6 : vvmfunct6 <-> bits(6) = { + VVM_VMADC <-> 0b010001, /* carry in, carry out */ + VVM_VMSBC <-> 0b010011 +} + +mapping clause encdec = VVMTYPE(funct6, vs2, vs1, vd) if haveVExt() + <-> encdec_vvmfunct6(funct6) @ 0b0 @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VVMTYPE(funct6, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask_carry(num_elem, 0b0, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_carry(num_elem, SEW, LMUL_pow, vd_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VVM_VMADC => unsigned(vs2_val[i]) + unsigned(vs1_val[i]) + unsigned(bool_to_bits(vm_val[i])) > 2 ^ SEW - 1, + VVM_VMSBC => unsigned(vs2_val[i]) - unsigned(vs1_val[i]) - unsigned(bool_to_bits(vm_val[i])) < 0 + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vvmtype_mnemonic : vvmfunct6 <-> string = { + VVM_VMADC <-> "vmadc.vvm", /* carry in, carry out */ + VVM_VMSBC <-> "vmsbc.vvm" +} + +mapping clause assembly = VVMTYPE(funct6, vs2, vs1, vd) + <-> vvmtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ sep() ^ "v0" + +/* ****************************** OPIVV (VVMCTYPE) ******************************* */ +/* VVMC instructions' destination is a mask register (e.g. carry out) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VVMCTYPE : (vvmcfunct6, regidx, regidx, regidx) + +mapping encdec_vvmcfunct6 : vvmcfunct6 <-> bits(6) = { + VVMC_VMADC <-> 0b010001, /* no carry in, carry out */ + VVMC_VMSBC <-> 0b010011 +} + +mapping clause encdec = VVMCTYPE(funct6, vs2, vs1, vd) if haveVExt() + <-> encdec_vvmcfunct6(funct6) @ 0b1 @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VVMCTYPE(funct6, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_carry(num_elem, SEW, LMUL_pow, vd_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VVMC_VMADC => unsigned(vs2_val[i]) + unsigned(vs1_val[i]) > 2 ^ SEW - 1, + VVMC_VMSBC => unsigned(vs2_val[i]) - unsigned(vs1_val[i]) < 0 + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vvmctype_mnemonic : vvmcfunct6 <-> string = { + VVMC_VMADC <-> "vmadc.vv", /* no carry in, carry out */ + VVMC_VMSBC <-> "vmsbc.vv" +} + +mapping clause assembly = VVMCTYPE(funct6, vs2, vs1, vd) + <-> vvmctype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) + +/* ****************************** OPIVV (VVMSTYPE) ******************************* */ +/* VVMS instructions' destination is a vector register (e.g. actual sum) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VVMSTYPE : (vvmsfunct6, regidx, regidx, regidx) + +mapping encdec_vvmsfunct6 : vvmsfunct6 <-> bits(6) = { + VVMS_VADC <-> 0b010000, /* carry in, no carry out */ + VVMS_VSBC <-> 0b010010 +} + +mapping clause encdec = VVMSTYPE(funct6, vs2, vs1, vd) if haveVExt() + <-> encdec_vvmsfunct6(funct6) @ 0b0 @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VVMSTYPE(funct6, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_masked(vd) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + /* for bypassing normal masking in init_masked_result */ + vec_trues : vector('n, dec, bool) = undefined; + foreach (i from 0 to (num_elem - 1)) { + vec_trues[i] = true + }; + + let vm_val : vector('n, dec, bool) = read_vmask_carry(num_elem, 0b0, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vec_trues); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VVMS_VADC => to_bits(SEW, unsigned(vs2_val[i]) + unsigned(vs1_val[i]) + unsigned(bool_to_bits(vm_val[i]))), + VVMS_VSBC => to_bits(SEW, unsigned(vs2_val[i]) - unsigned(vs1_val[i]) - unsigned(bool_to_bits(vm_val[i]))) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vvmstype_mnemonic : vvmsfunct6 <-> string = { + VVMS_VADC <-> "vadc.vvm", /* carry in, no carry out */ + VVMS_VSBC <-> "vsbc.vvm" +} + +mapping clause assembly = VVMSTYPE(funct6, vs2, vs1, vd) + <-> vvmstype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ sep() ^ "v0" + +/* ***************** OPIVV (Vector Integer Compare Instructions) ***************** */ +/* VVCMP instructions' destination is a mask register */ +union clause ast = VVCMPTYPE : (vvcmpfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_vvcmpfunct6 : vvcmpfunct6 <-> bits(6) = { + VVCMP_VMSEQ <-> 0b011000, + VVCMP_VMSNE <-> 0b011001, + VVCMP_VMSLTU <-> 0b011010, + VVCMP_VMSLT <-> 0b011011, + VVCMP_VMSLEU <-> 0b011100, + VVCMP_VMSLE <-> 0b011101 +} + +mapping clause encdec = VVCMPTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_vvcmpfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b000 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VVCMPTYPE(funct6, vm, vs2, vs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VVCMP_VMSEQ => vs2_val[i] == vs1_val[i], + VVCMP_VMSNE => vs2_val[i] != vs1_val[i], + VVCMP_VMSLTU => unsigned(vs2_val[i]) < unsigned(vs1_val[i]), + VVCMP_VMSLT => signed(vs2_val[i]) < signed(vs1_val[i]), + VVCMP_VMSLEU => unsigned(vs2_val[i]) <= unsigned(vs1_val[i]), + VVCMP_VMSLE => signed(vs2_val[i]) <= signed(vs1_val[i]) + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vvcmptype_mnemonic : vvcmpfunct6 <-> string = { + VVCMP_VMSEQ <-> "vmseq.vv", + VVCMP_VMSNE <-> "vmsne.vv", + VVCMP_VMSLTU <-> "vmsltu.vv", + VVCMP_VMSLT <-> "vmslt.vv", + VVCMP_VMSLEU <-> "vmsleu.vv", + VVCMP_VMSLE <-> "vmsle.vv" +} + +mapping clause assembly = VVCMPTYPE(funct6, vm, vs2, vs1, vd) + <-> vvcmptype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ******************************* OPIVX (VXMTYPE) ******************************* */ +/* VXM instructions' destination is a mask register (e.g. carry out) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VXMTYPE : (vxmfunct6, regidx, regidx, regidx) + +mapping encdec_vxmfunct6 : vxmfunct6 <-> bits(6) = { + VXM_VMADC <-> 0b010001, /* carry in, carry out */ + VXM_VMSBC <-> 0b010011 +} + +mapping clause encdec = VXMTYPE(funct6, vs2, rs1, vd) if haveVExt() + <-> encdec_vxmfunct6(funct6) @ 0b0 @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VXMTYPE(funct6, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask_carry(num_elem, 0b0, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_carry(num_elem, SEW, LMUL_pow, vd_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VXM_VMADC => unsigned(vs2_val[i]) + unsigned(rs1_val) + unsigned(bool_to_bits(vm_val[i])) > 2 ^ SEW - 1, + VXM_VMSBC => unsigned(vs2_val[i]) - unsigned(rs1_val) - unsigned(bool_to_bits(vm_val[i])) < 0 + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vxmtype_mnemonic : vxmfunct6 <-> string = { + VXM_VMADC <-> "vmadc.vxm", /* carry in, carry out */ + VXM_VMSBC <-> "vmsbc.vxm" +} + +mapping clause assembly = VXMTYPE(funct6, vs2, rs1, vd) + <-> vxmtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ sep() ^ "v0" + +/* ****************************** OPIVX (VXMCTYPE) ******************************* */ +/* VXMC instructions' destination is a mask register (e.g. carry out) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VXMCTYPE : (vxmcfunct6, regidx, regidx, regidx) + +mapping encdec_vxmcfunct6 : vxmcfunct6 <-> bits(6) = { + VXMC_VMADC <-> 0b010001, /* carry in, carry out */ + VXMC_VMSBC <-> 0b010011 +} + +mapping clause encdec = VXMCTYPE(funct6, vs2, rs1, vd) if haveVExt() + <-> encdec_vxmcfunct6(funct6) @ 0b1 @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VXMCTYPE(funct6, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_carry(num_elem, SEW, LMUL_pow, vd_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VXMC_VMADC => unsigned(vs2_val[i]) + unsigned(rs1_val) > 2 ^ SEW - 1, + VXMC_VMSBC => unsigned(vs2_val[i]) - unsigned(rs1_val) < 0 + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vxmctype_mnemonic : vxmcfunct6 <-> string = { + VXMC_VMADC <-> "vmadc.vx", /* carry in, carry out */ + VXMC_VMSBC <-> "vmsbc.vx" +} + +mapping clause assembly = VXMCTYPE(funct6, vs2, rs1, vd) + <-> vxmctype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) + +/* ****************************** OPIVX (VXMSTYPE) ******************************* */ +/* VXMS instructions' destination is a vector register (e.g. actual sum) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VXMSTYPE : (vxmsfunct6, regidx, regidx, regidx) + +mapping encdec_vxmsfunct6 : vxmsfunct6 <-> bits(6) = { + VXMS_VADC <-> 0b010000, /* carry in, no carry out */ + VXMS_VSBC <-> 0b010010 +} + +mapping clause encdec = VXMSTYPE(funct6, vs2, rs1, vd) if haveVExt() + <-> encdec_vxmsfunct6(funct6) @ 0b0 @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VXMSTYPE(funct6, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_masked(vd) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + /* for bypassing normal masking in init_masked_result */ + vec_trues : vector('n, dec, bool) = undefined; + foreach (i from 0 to (num_elem - 1)) { + vec_trues[i] = true + }; + + let vm_val : vector('n, dec, bool) = read_vmask_carry(num_elem, 0b0, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vec_trues); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VXMS_VADC => to_bits(SEW, unsigned(vs2_val[i]) + unsigned(rs1_val) + unsigned(bool_to_bits(vm_val[i]))), + VXMS_VSBC => to_bits(SEW, unsigned(vs2_val[i]) - unsigned(rs1_val) - unsigned(bool_to_bits(vm_val[i]))) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vxmstype_mnemonic : vxmsfunct6 <-> string = { + VXMS_VADC <-> "vadc.vxm", /* carry in, no carry out */ + VXMS_VSBC <-> "vsbc.vxm" +} + +mapping clause assembly = VXMSTYPE(funct6, vs2, rs1, vd) + <-> vxmstype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ sep() ^ "v0" + +/* ***************** OPIVX (Vector Integer Compare Instructions) ***************** */ +/* VXCMP instructions' destination is a mask register */ +union clause ast = VXCMPTYPE : (vxcmpfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_vxcmpfunct6 : vxcmpfunct6 <-> bits(6) = { + VXCMP_VMSEQ <-> 0b011000, + VXCMP_VMSNE <-> 0b011001, + VXCMP_VMSLTU <-> 0b011010, + VXCMP_VMSLT <-> 0b011011, + VXCMP_VMSLEU <-> 0b011100, + VXCMP_VMSLE <-> 0b011101, + VXCMP_VMSGTU <-> 0b011110, + VXCMP_VMSGT <-> 0b011111 +} + +mapping clause encdec = VXCMPTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_vxcmpfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b100 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VXCMPTYPE(funct6, vm, vs2, rs1, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : bits('m) = get_scalar(rs1, SEW); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VXCMP_VMSEQ => vs2_val[i] == rs1_val, + VXCMP_VMSNE => vs2_val[i] != rs1_val, + VXCMP_VMSLTU => unsigned(vs2_val[i]) < unsigned(rs1_val), + VXCMP_VMSLT => signed(vs2_val[i]) < signed(rs1_val), + VXCMP_VMSLEU => unsigned(vs2_val[i]) <= unsigned(rs1_val), + VXCMP_VMSLE => signed(vs2_val[i]) <= signed(rs1_val), + VXCMP_VMSGTU => unsigned(vs2_val[i]) > unsigned(rs1_val), + VXCMP_VMSGT => signed(vs2_val[i]) > signed(rs1_val) + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vxcmptype_mnemonic : vxcmpfunct6 <-> string = { + VXCMP_VMSEQ <-> "vmseq.vx", + VXCMP_VMSNE <-> "vmsne.vx", + VXCMP_VMSLTU <-> "vmsltu.vx", + VXCMP_VMSLT <-> "vmslt.vx", + VXCMP_VMSLEU <-> "vmsleu.vx", + VXCMP_VMSLE <-> "vmsle.vx", + VXCMP_VMSGTU <-> "vmsgtu.vx", + VXCMP_VMSGT <-> "vmsgt.vx" +} + +mapping clause assembly = VXCMPTYPE(funct6, vm, vs2, rs1, vd) + <-> vxcmptype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) + +/* ******************************* OPIVI (VIMTYPE) ******************************* */ +/* VIM instructions' destination is a mask register (e.g. carry out) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VIMTYPE : (vimfunct6, regidx, regidx, regidx) + +mapping encdec_vimfunct6 : vimfunct6 <-> bits(6) = { + VIM_VMADC <-> 0b010001 /* carry in, carry out */ +} + +mapping clause encdec = VIMTYPE(funct6, vs2, simm, vd) if haveVExt() + <-> encdec_vimfunct6(funct6) @ 0b0 @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VIMTYPE(funct6, vs2, simm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask_carry(num_elem, 0b0, 0b00000); + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_carry(num_elem, SEW, LMUL_pow, vd_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VIM_VMADC => unsigned(vs2_val[i]) + unsigned(imm_val) + unsigned(bool_to_bits(vm_val[i])) > 2 ^ SEW - 1 + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vimtype_mnemonic : vimfunct6 <-> string = { + VIM_VMADC <-> "vmadc.vim" /* carry in, carry out */ +} + +mapping clause assembly = VIMTYPE(funct6, vs2, simm, vd) + <-> vimtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) ^ sep() ^ "v0" + +/* ****************************** OPIVI (VIMCTYPE) ******************************* */ +/* VIMC instructions' destination is a mask register (e.g. carry out) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VIMCTYPE : (vimcfunct6, regidx, regidx, regidx) + +mapping encdec_vimcfunct6 : vimcfunct6 <-> bits(6) = { + VIMC_VMADC <-> 0b010001 /* carry in, carry out */ +} + +mapping clause encdec = VIMCTYPE(funct6, vs2, simm, vd) if haveVExt() + <-> encdec_vimcfunct6(funct6) @ 0b1 @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VIMCTYPE(funct6, vs2, simm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_carry(num_elem, SEW, LMUL_pow, vd_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VIMC_VMADC => unsigned(vs2_val[i]) + unsigned(imm_val) > 2 ^ SEW - 1 + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vimctype_mnemonic : vimcfunct6 <-> string = { + VIMC_VMADC <-> "vmadc.vi" /* Carry in, carry out */ +} + +mapping clause assembly = VIMCTYPE(funct6, vs2, simm, vd) + <-> vimctype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) + +/* ****************************** OPIVI (VIMSTYPE) ******************************* */ +/* VIMS instructions' destination is a vector register (e.g. actual sum) */ +/* Instructions with no carry out will set mask result to current mask value */ +/* May or may not read from source mask register (e.g. carry in) */ +union clause ast = VIMSTYPE : (vimsfunct6, regidx, regidx, regidx) + +mapping encdec_vimsfunct6 : vimsfunct6 <-> bits(6) = { + VIMS_VADC <-> 0b010000 /* Carry in, no carry out */ +} + +mapping clause encdec = VIMSTYPE(funct6, vs2, simm, vd) if haveVExt() + <-> encdec_vimsfunct6(funct6) @ 0b0 @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VIMSTYPE(funct6, vs2, simm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_masked(vd) then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + /* for bypassing normal masking in init_masked_result */ + vec_trues : vector('n, dec, bool) = undefined; + foreach (i from 0 to (num_elem - 1)) { + vec_trues[i] = true + }; + + let vm_val : vector('n, dec, bool) = read_vmask_carry(num_elem, 0b0, 0b00000); + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vd); + result : vector('n, dec, bits('m)) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result(num_elem, SEW, LMUL_pow, vd_val, vec_trues); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + result[i] = match funct6 { + VIMS_VADC => to_bits(SEW, unsigned(vs2_val[i]) + unsigned(imm_val) + unsigned(bool_to_bits(vm_val[i]))) + } + } + }; + + write_vreg(num_elem, SEW, LMUL_pow, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vimstype_mnemonic : vimsfunct6 <-> string = { + VIMS_VADC <-> "vadc.vim" /* Carry in, no carry out */ +} + +mapping clause assembly = VIMSTYPE(funct6, vs2, simm, vd) + <-> vimstype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) ^ sep() ^ "v0" + +/* ***************** OPIVI (Vector Integer Compare Instructions) ***************** */ +/* VICMP instructions' destination is a mask register */ +union clause ast = VICMPTYPE : (vicmpfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_vicmpfunct6 : vicmpfunct6 <-> bits(6) = { + VICMP_VMSEQ <-> 0b011000, + VICMP_VMSNE <-> 0b011001, + VICMP_VMSLEU <-> 0b011100, + VICMP_VMSLE <-> 0b011101, + VICMP_VMSGTU <-> 0b011110, + VICMP_VMSGT <-> 0b011111 +} + +mapping clause encdec = VICMPTYPE(funct6, vm, vs2, simm, vd) if haveVExt() + <-> encdec_vicmpfunct6(funct6) @ vm @ vs2 @ simm @ 0b011 @ vd @ 0b1010111 if haveVExt() + +function clause execute(VICMPTYPE(funct6, vm, vs2, simm, vd)) = { + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_vd_unmasked() then { handle_illegal(); return RETIRE_FAIL }; + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let imm_val : bits('m) = sign_extend(simm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VICMP_VMSEQ => vs2_val[i] == imm_val, + VICMP_VMSNE => vs2_val[i] != imm_val, + VICMP_VMSLEU => unsigned(vs2_val[i]) <= unsigned(imm_val), + VICMP_VMSLE => signed(vs2_val[i]) <= signed(imm_val), + VICMP_VMSGTU => unsigned(vs2_val[i]) > unsigned(imm_val), + VICMP_VMSGT => signed(vs2_val[i]) > signed(imm_val) + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping vicmptype_mnemonic : vicmpfunct6 <-> string = { + VICMP_VMSEQ <-> "vmseq.vi", + VICMP_VMSNE <-> "vmsne.vi", + VICMP_VMSLEU <-> "vmsleu.vi", + VICMP_VMSLE <-> "vmsle.vi", + VICMP_VMSGTU <-> "vmsgtu.vi", + VICMP_VMSGT <-> "vmsgt.vi" +} + +mapping clause assembly = VICMPTYPE(funct6, vm, vs2, simm, vd) + <-> vicmptype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ hex_bits_5(simm) ^ maybe_vmask(vm) + +/* ******************************* OPFVV (VVMTYPE) ******************************* */ +/* FVVM instructions' destination is a mask register */ +union clause ast = FVVMTYPE : (fvvmfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fvvmfunct6 : fvvmfunct6 <-> bits(6) = { + FVVM_VMFEQ <-> 0b011000, + FVVM_VMFLE <-> 0b011001, + FVVM_VMFLT <-> 0b011011, + FVVM_VMFNE <-> 0b011100 +} + +mapping clause encdec = FVVMTYPE(funct6, vm, vs2, vs1, vd) if haveVExt() + <-> encdec_fvvmfunct6(funct6) @ vm @ vs2 @ vs1 @ 0b001 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FVVMTYPE(funct6, vm, vs2, vs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_vd_unmasked(SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let vs1_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs1); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + FVVM_VMFEQ => fp_eq(vs2_val[i], vs1_val[i]), + FVVM_VMFNE => ~(fp_eq(vs2_val[i], vs1_val[i])), + FVVM_VMFLE => fp_le(vs2_val[i], vs1_val[i]), + FVVM_VMFLT => fp_lt(vs2_val[i], vs1_val[i]) + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fvvmtype_mnemonic : fvvmfunct6 <-> string = { + FVVM_VMFEQ <-> "vmfeq.vv", + FVVM_VMFLE <-> "vmfle.vv", + FVVM_VMFLT <-> "vmflt.vv", + FVVM_VMFNE <-> "vmfne.vv" +} + +mapping clause assembly = FVVMTYPE(funct6, vm, vs2, vs1, vd) + <-> fvvmtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ vreg_name(vs1) ^ maybe_vmask(vm) + +/* ******************************* OPFVF (VFMTYPE) ******************************* */ +/* VFM instructions' destination is a mask register */ +union clause ast = FVFMTYPE : (fvfmfunct6, bits(1), regidx, regidx, regidx) + +mapping encdec_fvfmfunct6 : fvfmfunct6 <-> bits(6) = { + VFM_VMFEQ <-> 0b011000, + VFM_VMFLE <-> 0b011001, + VFM_VMFLT <-> 0b011011, + VFM_VMFNE <-> 0b011100, + VFM_VMFGT <-> 0b011101, + VFM_VMFGE <-> 0b011111 +} + +mapping clause encdec = FVFMTYPE(funct6, vm, vs2, rs1, vd) if haveVExt() + <-> encdec_fvfmfunct6(funct6) @ vm @ vs2 @ rs1 @ 0b101 @ vd @ 0b1010111 if haveVExt() + +function clause execute(FVFMTYPE(funct6, vm, vs2, rs1, vd)) = { + let rm_3b = fcsr.FRM(); + let SEW = get_sew(); + let LMUL_pow = get_lmul_pow(); + let num_elem = get_num_elem(LMUL_pow, SEW); + + if illegal_fp_vd_unmasked(SEW, rm_3b) then { handle_illegal(); return RETIRE_FAIL }; + assert(SEW != 8); + + let 'n = num_elem; + let 'm = SEW; + + let vm_val : vector('n, dec, bool) = read_vmask(num_elem, vm, 0b00000); + let rs1_val : bits('m) = get_scalar_fp(rs1, 'm); + let vs2_val : vector('n, dec, bits('m)) = read_vreg(num_elem, SEW, LMUL_pow, vs2); + let vd_val : vector('n, dec, bool) = read_vmask(num_elem, 0b0, vd); + result : vector('n, dec, bool) = undefined; + mask : vector('n, dec, bool) = undefined; + + (result, mask) = init_masked_result_cmp(num_elem, SEW, LMUL_pow, vd_val, vm_val); + + foreach (i from 0 to (num_elem - 1)) { + if mask[i] then { + let res : bool = match funct6 { + VFM_VMFEQ => fp_eq(vs2_val[i], rs1_val), + VFM_VMFNE => ~(fp_eq(vs2_val[i], rs1_val)), + VFM_VMFLE => fp_le(vs2_val[i], rs1_val), + VFM_VMFLT => fp_lt(vs2_val[i], rs1_val), + VFM_VMFGE => fp_ge(vs2_val[i], rs1_val), + VFM_VMFGT => fp_gt(vs2_val[i], rs1_val) + }; + result[i] = res + } + }; + + write_vmask(num_elem, vd, result); + vstart = zeros(); + RETIRE_SUCCESS +} + +mapping fvfmtype_mnemonic : fvfmfunct6 <-> string = { + VFM_VMFEQ <-> "vmfeq.vf", + VFM_VMFLE <-> "vmfle.vf", + VFM_VMFLT <-> "vmflt.vf", + VFM_VMFNE <-> "vmfne.vf", + VFM_VMFGT <-> "vmfgt.vf", + VFM_VMFGE <-> "vmfge.vf" +} + +mapping clause assembly = FVFMTYPE(funct6, vm, vs2, rs1, vd) + <-> fvfmtype_mnemonic(funct6) ^ spc() ^ vreg_name(vd) ^ sep() ^ vreg_name(vs2) ^ sep() ^ reg_name(rs1) ^ maybe_vmask(vm) diff --git a/model/riscv_insts_vext_vset.sail b/model/riscv_insts_vext_vset.sail new file mode 100644 index 000000000..960036237 --- /dev/null +++ b/model/riscv_insts_vext_vset.sail @@ -0,0 +1,213 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* ******************************************************************************* */ +/* This file implements part of the vector extension. */ +/* Chapter 6: Configuration-Setting Instructions */ +/* ******************************************************************************* */ + +mapping sew_flag : string <-> bits(3) = { + "e8" <-> 0b000, + "e16" <-> 0b001, + "e32" <-> 0b010, + "e64" <-> 0b011 +} + +mapping maybe_lmul_flag : string <-> bits(3) = { + "" <-> 0b000, /* m1 by default */ + sep() ^ "mf8" <-> 0b101, + sep() ^ "mf4" <-> 0b110, + sep() ^ "mf2" <-> 0b111, + sep() ^ "m1" <-> 0b000, + sep() ^ "m2" <-> 0b001, + sep() ^ "m4" <-> 0b010, + sep() ^ "m8" <-> 0b011 +} + +mapping maybe_ta_flag : string <-> bits(1) = { + "" <-> 0b0, /* tu by default */ + sep() ^ "ta" <-> 0b1, + sep() ^ "tu" <-> 0b0 +} + +mapping maybe_ma_flag : string <-> bits(1) = { + "" <-> 0b0, /* mu by default */ + sep() ^ "ma" <-> 0b1, + sep() ^ "mu" <-> 0b0 +} + +/* ****************************** vsetvli & vsetvl ******************************* */ +union clause ast = VSET_TYPE : (vsetop, bits(1), bits(1), bits(3), bits(3), regidx, regidx) + +mapping encdec_vsetop : vsetop <-> bits(4) ={ + VSETVLI <-> 0b0000, + VSETVL <-> 0b1000 +} + +mapping clause encdec = VSET_TYPE(op, ma, ta, sew, lmul, rs1, rd) if haveVExt() + <-> encdec_vsetop(op) @ ma @ ta @ sew @ lmul @ rs1 @ 0b111 @ rd @ 0b1010111 if haveVExt() + +function clause execute VSET_TYPE(op, ma, ta, sew, lmul, rs1, rd) = { + let VLEN_pow = get_vlen_pow(); + let ELEN_pow = get_elen_pow(); + let LMUL_pow_ori = get_lmul_pow(); + let SEW_pow_ori = get_sew_pow(); + let ratio_pow_ori = SEW_pow_ori - LMUL_pow_ori; + + /* set vtype */ + match op { + VSETVLI => { + vtype->bits() = 0b0 @ zeros(sizeof(xlen) - 9) @ ma @ ta @ sew @ lmul + }, + VSETVL => { + let rs2 : regidx = sew[1 .. 0] @ lmul; + vtype->bits() = X(rs2) + } + }; + + /* check legal SEW and LMUL and calculate VLMAX */ + let LMUL_pow_new = get_lmul_pow(); + let SEW_pow_new = get_sew_pow(); + if SEW_pow_new > LMUL_pow_new + ELEN_pow then { + /* Note: Implementations can set vill or trap if the vtype setting is not supported. + * TODO: configuration support for both solutions + */ + vtype->bits() = 0b1 @ zeros(sizeof(xlen) - 1); /* set vtype.vill */ + vl = zeros(); + print_reg("CSR vtype <- " ^ BitStr(vtype.bits())); + print_reg("CSR vl <- " ^ BitStr(vl)); + return RETIRE_SUCCESS + }; + let VLMAX = int_power(2, VLEN_pow + LMUL_pow_new - SEW_pow_new); + + /* set vl according to VLMAX and AVL */ + if (rs1 != 0b00000) then { /* normal stripmining */ + let rs1_val = X(rs1); + let AVL = unsigned(rs1_val); + vl = if AVL <= VLMAX then to_bits(sizeof(xlen), AVL) + else if AVL < 2 * VLMAX then to_bits(sizeof(xlen), (AVL + 1) / 2) + else to_bits(sizeof(xlen), VLMAX); + /* Note: ceil(AVL / 2) ≤ vl ≤ VLMAX when VLMAX < AVL < (2 * VLMAX) + * TODO: configuration support for either using ceil(AVL / 2) or VLMAX + */ + X(rd) = vl; + } else if (rd != 0b00000) then { /* set vl to VLMAX */ + let AVL = unsigned(ones(sizeof(xlen))); + vl = to_bits(sizeof(xlen), VLMAX); + X(rd) = vl; + } else { /* keep existing vl */ + let AVL = unsigned(vl); + let ratio_pow_new = SEW_pow_new - LMUL_pow_new; + if (ratio_pow_new != ratio_pow_ori) then { + /* Note: Implementations can set vill or trap if the vtype setting is not supported. + * TODO: configuration support for both solutions + */ + vtype->bits() = 0b1 @ zeros(sizeof(xlen) - 1); /* set vtype.vill */ + vl = zeros(); + } + }; + print_reg("CSR vtype <- " ^ BitStr(vtype.bits())); + print_reg("CSR vl <- " ^ BitStr(vl)); + + /* reset vstart to 0 */ + vstart = zeros(); + print_reg("CSR vstart <- " ^ BitStr(vstart)); + + RETIRE_SUCCESS +} + +mapping vsettype_mnemonic : vsetop <-> string ={ + VSETVLI <-> "vsetvli", + VSETVL <-> "vsetvli" +} + +mapping clause assembly = VSET_TYPE(op, ma, ta, sew, lmul, rs1, rd) + <-> vsettype_mnemonic(op) ^ spc() ^ reg_name(rd) ^ sep() ^ reg_name(rs1) ^ sep() ^ sew_flag(sew) ^ maybe_lmul_flag(lmul) ^ maybe_ta_flag(ta) ^ maybe_ma_flag(ma) + +/* ********************************* vsetivli ************************************ */ +union clause ast = VSETI_TYPE : ( bits(1), bits(1), bits(3), bits(3), regidx, regidx) + +mapping clause encdec = VSETI_TYPE(ma, ta, sew, lmul, uimm, rd) if haveVExt() + <-> 0b1100 @ ma @ ta @ sew @ lmul @ uimm @ 0b111 @ rd @ 0b1010111 if haveVExt() + +function clause execute VSETI_TYPE(ma, ta, sew, lmul, uimm, rd) = { + let VLEN_pow = get_vlen_pow(); + let ELEN_pow = get_elen_pow(); + let LMUL_pow_ori = get_lmul_pow(); + let SEW_pow_ori = get_sew_pow(); + let ratio_pow_ori = SEW_pow_ori - LMUL_pow_ori; + + /* set vtype */ + vtype->bits() = 0b0 @ zeros(sizeof(xlen) - 9) @ ma @ ta @ sew @ lmul; + + /* check legal SEW and LMUL and calculate VLMAX */ + let LMUL_pow_new = get_lmul_pow(); + let SEW_pow_new = get_sew_pow(); + if SEW_pow_new > LMUL_pow_new + ELEN_pow then { + /* Note: Implementations can set vill or trap if the vtype setting is not supported. + * TODO: configuration support for both solutions + */ + vtype->bits() = 0b1 @ zeros(sizeof(xlen) - 1); /* set vtype.vill */ + vl = zeros(); + print_reg("CSR vtype <- " ^ BitStr(vtype.bits())); + print_reg("CSR vl <- " ^ BitStr(vl)); + return RETIRE_SUCCESS + }; + let VLMAX = int_power(2, VLEN_pow + LMUL_pow_new - SEW_pow_new); + let AVL = unsigned(uimm); /* AVL is encoded as 5-bit zero-extended imm in the rs1 field */ + + /* set vl according to VLMAX and AVL */ + vl = if AVL <= VLMAX then to_bits(sizeof(xlen), AVL) + else if AVL < 2 * VLMAX then to_bits(sizeof(xlen), (AVL + 1) / 2) + else to_bits(sizeof(xlen), VLMAX); + /* Note: ceil(AVL / 2) ≤ vl ≤ VLMAX when VLMAX < AVL < (2 * VLMAX) + * TODO: configuration support for either using ceil(AVL / 2) or VLMAX + */ + X(rd) = vl; + print_reg("CSR vtype <- " ^ BitStr(vtype.bits())); + print_reg("CSR vl <- " ^ BitStr(vl)); + + /* reset vstart to 0 */ + vstart = zeros(); + print_reg("CSR vstart <- " ^ BitStr(vstart)); + + RETIRE_SUCCESS +} + +mapping clause assembly = VSETI_TYPE(ma, ta, sew, lmul, uimm, rd) + <-> "vsetivli" ^ spc() ^ reg_name(rd) ^ sep() ^ hex_bits_5(uimm) ^ sep() ^ sew_flag(sew) ^ maybe_lmul_flag(lmul) ^ maybe_ta_flag(ta) ^ maybe_ma_flag(ma) diff --git a/model/riscv_insts_zicsr.sail b/model/riscv_insts_zicsr.sail index f6e767265..1c4ad7142 100644 --- a/model/riscv_insts_zicsr.sail +++ b/model/riscv_insts_zicsr.sail @@ -135,6 +135,15 @@ function readCSR csr : csreg -> xlenbits = { (0xB80, 32) => mcycle[63 .. 32], (0xB82, 32) => minstret[63 .. 32], + /* vector */ + (0x008, _) => zero_extend(vstart), + (0x009, _) => zero_extend(vxsat), + (0x00A, _) => zero_extend(vxrm), + (0x00F, _) => zero_extend(vcsr.bits()), + (0xC20, _) => vl, + (0xC21, _) => vtype.bits(), + (0xC22, _) => vlenb, + /* trigger/debug */ (0x7a0, _) => ~(tselect), /* this indicates we don't have any trigger support */ @@ -243,6 +252,15 @@ function writeCSR (csr : csreg, value : xlenbits) -> unit = { /* user mode: seed (entropy source). writes are ignored */ (0x015, _) => write_seed_csr(), + /* vector */ + (0x008, _) => { let vstart_length = get_vlen_pow(); vstart = zero_extend(16, value[(vstart_length - 1) .. 0]); Some(zero_extend(vstart)) }, + (0x009, _) => { vxsat = value[0 .. 0]; Some(zero_extend(vxsat)) }, + (0x00A, _) => { vxrm = value[1 .. 0]; Some(zero_extend(vxrm)) }, + (0x00F, _) => { vcsr->bits() = value[2 ..0]; Some(zero_extend(vcsr.bits())) }, + (0xC20, _) => { vl = value; Some(vl) }, + (0xC21, _) => { vtype->bits() = value; Some(vtype.bits()) }, + (0xC22, _) => { vlenb = value; Some(vlenb) }, + _ => ext_write_CSR(csr, value) }; match res { diff --git a/model/riscv_softfloat_interface.sail b/model/riscv_softfloat_interface.sail index 3be28c8c0..98a9a02cc 100644 --- a/model/riscv_softfloat_interface.sail +++ b/model/riscv_softfloat_interface.sail @@ -306,6 +306,7 @@ function riscv_ui64ToF16 (rm, v) = { (float_fflags[4 .. 0], float_result[15 .. 0]) } + val extern_f32ToI32 = {c: "softfloat_f32toi32", ocaml: "Softfloat.f32_to_i32", lem: "softfloat_f32_to_i32"} : (bits_rm, bits_S) -> unit val riscv_f32ToI32 : (bits_rm, bits_S) -> (bits_fflags, bits_W) effect {rreg} function riscv_f32ToI32 (rm, v) = { diff --git a/model/riscv_sys_control.sail b/model/riscv_sys_control.sail index d057ad6c0..2f9d4ce21 100644 --- a/model/riscv_sys_control.sail +++ b/model/riscv_sys_control.sail @@ -554,12 +554,13 @@ function init_sys() -> unit = { mhartid = zero_extend(0b0); misa->MXL() = arch_to_bits(if sizeof(xlen) == 32 then RV32 else RV64); - misa->A() = 0b1; /* atomics */ - misa->C() = bool_to_bits(sys_enable_rvc()); /* RVC */ - misa->I() = 0b1; /* base integer ISA */ - misa->M() = 0b1; /* integer multiply/divide */ - misa->U() = 0b1; /* user-mode */ - misa->S() = 0b1; /* supervisor-mode */ + misa->A() = 0b1; /* atomics */ + misa->C() = bool_to_bits(sys_enable_rvc()); /* RVC */ + misa->I() = 0b1; /* base integer ISA */ + misa->M() = 0b1; /* integer multiply/divide */ + misa->U() = 0b1; /* user-mode */ + misa->S() = 0b1; /* supervisor-mode */ + misa->V() = bool_to_bits(sys_enable_vext()); /* vector extension */ if sys_enable_fdext() & sys_enable_zfinx() then internal_error(__FILE__, __LINE__, "F and Zfinx cannot both be enabled!"); @@ -598,6 +599,26 @@ function init_sys() -> unit = { minstret = zero_extend(0b0); minstret_increment = true; + /* initialize vector csrs */ + elen = 0b1; /* ELEN=64 as the common case */ + vlen = 0b0100; /* VLEN=512 as a default value */ + vlenb = to_bits(sizeof(xlen), 2 ^ (get_vlen_pow() - 3)); /* vlenb holds the constant value VLEN/8 */ + /* VLEN value needs to be manually changed currently. + * See riscv_vlen.sail for details. + */ + vstart = zero_extend(0b0); + vxsat = 0b0; + vxrm = 0b00; + vcsr->vxrm() = vxrm; + vcsr->vxsat() = vxsat; + vl = zero_extend(0b0); + vtype->vill() = 0b1; + vtype->reserved() = zero_extend(0b0); + vtype->vma() = 0b0; + vtype->vta() = 0b0; + vtype->vsew() = 0b000; + vtype->vlmul() = 0b000; + init_pmp(); // log compatibility with spike diff --git a/model/riscv_sys_regs.sail b/model/riscv_sys_regs.sail index fedb02e9e..32a8efe7f 100644 --- a/model/riscv_sys_regs.sail +++ b/model/riscv_sys_regs.sail @@ -150,6 +150,8 @@ val sys_enable_fdext = {c: "sys_enable_fdext", ocaml: "Platform.enable_fdext", _ val sys_enable_zfinx = {c: "sys_enable_zfinx", ocaml: "Platform.enable_zfinx", _: "sys_enable_zfinx"} : unit -> bool /* whether the N extension was enabled at boot */ val sys_enable_next = {c: "sys_enable_next", ocaml: "Platform.enable_next", _: "sys_enable_next"} : unit -> bool +/* whether misa.v was enabled at boot */ +val sys_enable_vext = {c: "sys_enable_vext", ocaml: "Platform.enable_vext", _: "sys_enable_vext"} : unit -> bool /* This function allows an extension to veto a write to Misa if it would violate an alignment restriction on @@ -244,6 +246,7 @@ bitfield Mstatus : xlenbits = { FS : 14 .. 13, MPP : 12 .. 11, + VS : 10 .. 9, SPP : 8, MPIE : 7, @@ -297,7 +300,7 @@ function legalize_mstatus(o : Mstatus, v : xlenbits) -> Mstatus = { * that does not have a matching bitfield entry. All bits above 32 are handled * explicitly later. */ - let m : Mstatus = Mk_Mstatus(zero_extend(v[22 .. 11] @ 0b00 @ v[8 .. 7] @ 0b0 @ v[5 .. 3] @ 0b0 @ v[1 .. 0])); + let m : Mstatus = Mk_Mstatus(zero_extend(v[22 .. 7] @ 0b0 @ v[5 .. 3] @ 0b0 @ v[1 .. 0])); /* We don't have any extension context yet. */ let m = update_XS(m, extStatus_to_bits(Off)); @@ -308,7 +311,8 @@ function legalize_mstatus(o : Mstatus, v : xlenbits) -> Mstatus = { * FIXME: This should be made a platform parameter. */ let m = if sys_enable_zfinx() then update_FS(m, extStatus_to_bits(Off)) else m; - let dirty = extStatus_of_bits(m.FS()) == Dirty | extStatus_of_bits(m.XS()) == Dirty; + let dirty = extStatus_of_bits(m.FS()) == Dirty | extStatus_of_bits(m.XS()) == Dirty | + extStatus_of_bits(m.VS()) == Dirty; let m = update_SD(m, bool_to_bits(dirty)); /* We don't support dynamic changes to SXL and UXL. */ @@ -357,6 +361,8 @@ function haveFExt() -> bool = (misa.F() == 0b1) & (mstatus.FS() != 0b00) function haveDExt() -> bool = (misa.D() == 0b1) & (mstatus.FS() != 0b00) /* Zfh (half-precision) extension depends on misa.F and mstatus.FS */ function haveZfh() -> bool = (misa.F() == 0b1) & (mstatus.FS() != 0b00) +/* V extension has to enable both via misa.V as well as mstatus.VS */ +function haveVExt() -> bool = (misa.V() == 0b1) & (mstatus.VS() != 0b00) /* Zhinx, Zfinx and Zdinx extensions (TODO: gate FCSR access on [mhs]stateen0 bit 1 when implemented) */ function haveZhinx() -> bool = sys_enable_zfinx() @@ -592,6 +598,7 @@ bitfield Sstatus : xlenbits = { SUM : 18, XS : 16 .. 15, FS : 14 .. 13, + VS : 10 .. 9, SPP : 8, SPIE : 5, UPIE : 4, @@ -619,6 +626,7 @@ function lower_mstatus(m : Mstatus) -> Sstatus = { let s = update_SUM(s, m.SUM()); let s = update_XS(s, m.XS()); let s = update_FS(s, m.FS()); + let s = update_VS(s, m.VS()); let s = update_SPP(s, m.SPP()); let s = update_SPIE(s, m.SPIE()); let s = update_UPIE(s, m.UPIE()); @@ -634,7 +642,9 @@ function lift_sstatus(m : Mstatus, s : Sstatus) -> Mstatus = { let m = update_XS(m, s.XS()); // See comment for mstatus.FS. let m = update_FS(m, s.FS()); - let dirty = extStatus_of_bits(m.FS()) == Dirty | extStatus_of_bits(m.XS()) == Dirty; + let m = update_VS(m, s.VS()); + let dirty = extStatus_of_bits(m.FS()) == Dirty | extStatus_of_bits(m.XS()) == Dirty | + extStatus_of_bits(m.VS()) == Dirty; let m = update_SD(m, bool_to_bits(dirty)); let m = update_SPP(m, s.SPP()); @@ -830,3 +840,86 @@ function read_seed_csr() -> xlenbits = { /* Writes to the seed CSR are ignored */ function write_seed_csr () -> option(xlenbits) = None() + +/* vector csrs */ +register vstart : bits(16) /* use the largest possible length of vstart */ +register vxsat : bits(1) +register vxrm : bits(2) +register vl : xlenbits +register vlenb : xlenbits + +bitfield Vtype : xlenbits = { + vill : xlen - 1, + reserved : xlen - 2 .. 8, + vma : 7, + vta : 6, + vsew : 5 .. 3, + vlmul : 2 .. 0 +} +register vtype : Vtype + +/* the dynamic selected element width (SEW) */ +/* this returns the power of 2 for SEW */ +val get_sew_pow : unit -> {|3, 4, 5, 6|} effect {escape, rreg} +function get_sew_pow() = { + let SEW_pow : {|3, 4, 5, 6|} = match vtype.vsew() { + 0b000 => 3, + 0b001 => 4, + 0b010 => 5, + 0b011 => 6, + _ => {assert(false, "invalid vsew field in vtype"); 0} + }; + SEW_pow +} +/* this returns the actual value of SEW */ +val get_sew : unit -> {|8, 16, 32, 64|} effect {escape, rreg} +function get_sew() = { + match get_sew_pow() { + 3 => 8, + 4 => 16, + 5 => 32, + 6 => 64 + } +} +/* this returns the value of SEW in bytes */ +val get_sew_bytes : unit -> {|1, 2, 4, 8|} effect {escape, rreg} +function get_sew_bytes() = { + match get_sew_pow() { + 3 => 1, + 4 => 2, + 5 => 4, + 6 => 8 + } +} + +/* the vector register group multiplier (LMUL) */ +/* this returns the power of 2 for LMUL */ +val get_lmul_pow : unit -> {|-3, -2, -1, 0, 1, 2, 3|} effect {escape, rreg} +function get_lmul_pow() = { + match vtype.vlmul() { + 0b101 => -3, + 0b110 => -2, + 0b111 => -1, + 0b000 => 0, + 0b001 => 1, + 0b010 => 2, + 0b011 => 3, + _ => {assert(false, "invalid vlmul field in vtype"); 0} + } +} + +enum agtype = { UNDISTURBED, AGNOSTIC } + +val decode_agtype : bits(1) -> agtype +function decode_agtype(ag) = { + match ag { + 0b0 => UNDISTURBED, + 0b1 => AGNOSTIC + } +} + +val get_vtype_vma : unit -> agtype effect {rreg} +function get_vtype_vma() = decode_agtype(vtype.vma()) + +val get_vtype_vta : unit -> agtype effect {rreg} +function get_vtype_vta() = decode_agtype(vtype.vta()) diff --git a/model/riscv_vext_control.sail b/model/riscv_vext_control.sail new file mode 100755 index 000000000..0fc1660e9 --- /dev/null +++ b/model/riscv_vext_control.sail @@ -0,0 +1,58 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +function clause ext_is_CSR_defined (0x008, _) = true +function clause ext_is_CSR_defined (0xC20, _) = true +function clause ext_is_CSR_defined (0xC21, _) = true +function clause ext_is_CSR_defined (0xC22, _) = true + +function clause ext_is_CSR_defined (0x009, _) = true +function clause ext_is_CSR_defined (0x00A, _) = true +function clause ext_is_CSR_defined (0x00F, _) = true + +function clause ext_read_CSR (0x009) = Some (zero_extend(vcsr.vxsat())) +function clause ext_read_CSR (0x00A) = Some (zero_extend(vcsr.vxrm())) +function clause ext_read_CSR (0x00F) = Some (zero_extend(vcsr.bits())) + +function clause ext_read_CSR (0x009) = Some (zero_extend(vcsr.vxsat())) +function clause ext_read_CSR (0x00A) = Some (zero_extend(vcsr.vxrm())) +function clause ext_read_CSR (0x00F) = Some (zero_extend(vcsr.bits())) + +function clause ext_write_CSR (0x009, value) = { ext_write_vcsr (vcsr.vxrm(), value[0 .. 0]); Some(zero_extend(vcsr.vxsat())) } +function clause ext_write_CSR (0x00A, value) = { ext_write_vcsr (value[1 .. 0], vcsr.vxsat()); Some(zero_extend(vcsr.vxrm())) } +function clause ext_write_CSR (0x00F, value) = { ext_write_vcsr (value [2 .. 1], value [0 .. 0]); Some(zero_extend(vcsr.bits())) } diff --git a/model/riscv_vext_regs.sail b/model/riscv_vext_regs.sail new file mode 100644 index 000000000..9b5d6e9bf --- /dev/null +++ b/model/riscv_vext_regs.sail @@ -0,0 +1,463 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* vector registers */ +register vr0 : vregtype +register vr1 : vregtype +register vr2 : vregtype +register vr3 : vregtype +register vr4 : vregtype +register vr5 : vregtype +register vr6 : vregtype +register vr7 : vregtype +register vr8 : vregtype +register vr9 : vregtype +register vr10 : vregtype +register vr11 : vregtype +register vr12 : vregtype +register vr13 : vregtype +register vr14 : vregtype +register vr15 : vregtype +register vr16 : vregtype +register vr17 : vregtype +register vr18 : vregtype +register vr19 : vregtype +register vr20 : vregtype +register vr21 : vregtype +register vr22 : vregtype +register vr23 : vregtype +register vr24 : vregtype +register vr25 : vregtype +register vr26 : vregtype +register vr27 : vregtype +register vr28 : vregtype +register vr29 : vregtype +register vr30 : vregtype +register vr31 : vregtype + +val vreg_name : bits(5) <-> string +mapping vreg_name = { + 0b00000 <-> "v0", + 0b00001 <-> "v1", + 0b00010 <-> "v2", + 0b00011 <-> "v3", + 0b00100 <-> "v4", + 0b00101 <-> "v5", + 0b00110 <-> "v6", + 0b00111 <-> "v7", + 0b01000 <-> "v8", + 0b01001 <-> "v9", + 0b01010 <-> "v10", + 0b01011 <-> "v11", + 0b01100 <-> "v12", + 0b01101 <-> "v13", + 0b01110 <-> "v14", + 0b01111 <-> "v15", + 0b10000 <-> "v16", + 0b10001 <-> "v17", + 0b10010 <-> "v18", + 0b10011 <-> "v19", + 0b10100 <-> "v20", + 0b10101 <-> "v21", + 0b10110 <-> "v22", + 0b10111 <-> "v23", + 0b11000 <-> "v24", + 0b11001 <-> "v25", + 0b11010 <-> "v26", + 0b11011 <-> "v27", + 0b11100 <-> "v28", + 0b11101 <-> "v29", + 0b11110 <-> "v30", + 0b11111 <-> "v31" +} + +function dirty_v_context() -> unit = { + assert(sys_enable_vext()); + mstatus->VS() = extStatus_to_bits(Dirty); + mstatus->SD() = 0b1 +} + +function dirty_v_context_if_present() -> unit = { + if sys_enable_vext() then dirty_v_context() +} + +val rV : forall 'n, 0 <= 'n < 32. regno('n) -> vregtype effect {rreg, escape} +function rV r = { + let zero_vreg : vregtype = zeros(); + let v : vregtype = + match r { + 0 => vr0, + 1 => vr1, + 2 => vr2, + 3 => vr3, + 4 => vr4, + 5 => vr5, + 6 => vr6, + 7 => vr7, + 8 => vr8, + 9 => vr9, + 10 => vr10, + 11 => vr11, + 12 => vr12, + 13 => vr13, + 14 => vr14, + 15 => vr15, + 16 => vr16, + 17 => vr17, + 18 => vr18, + 19 => vr19, + 20 => vr20, + 21 => vr21, + 22 => vr22, + 23 => vr23, + 24 => vr24, + 25 => vr25, + 26 => vr26, + 27 => vr27, + 28 => vr28, + 29 => vr29, + 30 => vr30, + 31 => vr31, + _ => {assert(false, "invalid vector register number"); zero_vreg} + }; + v +} + +val wV : forall 'n, 0 <= 'n < 32. (regno('n), vregtype) -> unit effect {rreg, wreg, escape} +function wV (r, in_v) = { + let v = in_v; + match r { + 0 => vr0 = v, + 1 => vr1 = v, + 2 => vr2 = v, + 3 => vr3 = v, + 4 => vr4 = v, + 5 => vr5 = v, + 6 => vr6 = v, + 7 => vr7 = v, + 8 => vr8 = v, + 9 => vr9 = v, + 10 => vr10 = v, + 11 => vr11 = v, + 12 => vr12 = v, + 13 => vr13 = v, + 14 => vr14 = v, + 15 => vr15 = v, + 16 => vr16 = v, + 17 => vr17 = v, + 18 => vr18 = v, + 19 => vr19 = v, + 20 => vr20 = v, + 21 => vr21 = v, + 22 => vr22 = v, + 23 => vr23 = v, + 24 => vr24 = v, + 25 => vr25 = v, + 26 => vr26 = v, + 27 => vr27 = v, + 28 => vr28 = v, + 29 => vr29 = v, + 30 => vr30 = v, + 31 => vr31 = v, + _ => assert(false, "invalid vector register number") + }; + + dirty_v_context(); + + let VLEN = unsigned(vlenb) * 8; + assert(0 < VLEN & VLEN <= sizeof(vlenmax)); + if get_config_print_reg() + then print_reg("v" ^ string_of_int(r) ^ " <- " ^ BitStr(v[VLEN - 1 .. 0])); +} + +function rV_bits(i: bits(5)) -> vregtype = rV(unsigned(i)) + +function wV_bits(i: bits(5), data: vregtype) -> unit = { + wV(unsigned(i)) = data +} + +overload V = {rV_bits, wV_bits, rV, wV} + +val init_vregs : unit -> unit effect {wreg} +function init_vregs () = { + let zero_vreg : vregtype = zeros(); + vr0 = zero_vreg; + vr1 = zero_vreg; + vr2 = zero_vreg; + vr3 = zero_vreg; + vr4 = zero_vreg; + vr5 = zero_vreg; + vr6 = zero_vreg; + vr7 = zero_vreg; + vr8 = zero_vreg; + vr9 = zero_vreg; + vr10 = zero_vreg; + vr11 = zero_vreg; + vr12 = zero_vreg; + vr13 = zero_vreg; + vr14 = zero_vreg; + vr15 = zero_vreg; + vr16 = zero_vreg; + vr17 = zero_vreg; + vr18 = zero_vreg; + vr19 = zero_vreg; + vr20 = zero_vreg; + vr21 = zero_vreg; + vr22 = zero_vreg; + vr23 = zero_vreg; + vr24 = zero_vreg; + vr25 = zero_vreg; + vr26 = zero_vreg; + vr27 = zero_vreg; + vr28 = zero_vreg; + vr29 = zero_vreg; + vr30 = zero_vreg; + vr31 = zero_vreg +} + +/* Vector CSR */ +bitfield Vcsr : bits(3) = { + vxrm : 2 .. 1, + vxsat : 0 +} +register vcsr : Vcsr + +val ext_write_vcsr : (bits(2), bits(1)) -> unit effect {rreg, wreg} +function ext_write_vcsr (vxrm_val, vxsat_val) = { + vcsr->vxrm() = vxrm_val; /* Note: frm can be an illegal value, 101, 110, 111 */ + vcsr->vxsat() = vxsat_val; + dirty_v_context_if_present() +} + +/* num_elem means max(VLMAX,VLEN/SEW)) according to Section 5.4 of RVV spec */ +val get_num_elem : (int, int) -> nat effect {escape, rreg} +function get_num_elem(LMUL_pow, SEW) = { + let VLEN = unsigned(vlenb) * 8; + let LMUL_pow_reg = if LMUL_pow < 0 then 0 else LMUL_pow; + /* Ignore lmul < 1 so that the entire vreg is read, allowing all masking to + * be handled in init_masked_result */ + let num_elem = int_power(2, LMUL_pow_reg) * VLEN / SEW; + assert(num_elem > 0); + num_elem +} + +/* Reads a single vreg into multiple elements */ +val read_single_vreg : forall 'n 'm, 'n >= 0. (int('n), int('m), regidx) -> vector('n, dec, bits('m)) effect {escape, rreg, undef} +function read_single_vreg(num_elem, SEW, vrid) = { + let bv : vregtype = V(vrid); + var result : vector('n, dec, bits('m)) = undefined; + + assert(8 <= SEW & SEW <= 64); + foreach (i from 0 to (num_elem - 1)) { + let start_index = i * SEW; + result[i] = slice(bv, start_index, SEW); + }; + + result +} + +/* Writes multiple elements into a single vreg */ +val write_single_vreg : forall 'n 'm, 'n >= 0. (int('n), int('m), regidx, vector('n, dec, bits('m))) -> unit effect {escape, rreg, wreg} +function write_single_vreg(num_elem, SEW, vrid, v) = { + r : vregtype = zeros(); + + assert(8 <= SEW & SEW <= 64); + foreach (i from (num_elem - 1) downto 0) { + r = r << SEW; + r = r | zero_extend(v[i]); + }; + + V(vrid) = r +} + +/* The general vreg reading operation with num_elem as max(VLMAX,VLEN/SEW)) */ +val read_vreg : forall 'n 'm 'p, 'n >= 0. (int('n), int('m), int('p), regidx) -> vector('n, dec, bits('m)) effect {escape, rreg, undef} +function read_vreg(num_elem, SEW, LMUL_pow, vrid) = { + var result : vector('n, dec, bits('m)) = undefined; + let VLEN = unsigned(vlenb) * 8; + let LMUL_pow_reg = if LMUL_pow < 0 then 0 else LMUL_pow; + + /* Check for valid vrid */ + if unsigned(vrid) + 2 ^ LMUL_pow_reg > 32 then { + /* vrid would read past largest vreg (v31) */ + assert(false, "invalid register group: vrid overflow the largest number") + } else if unsigned(vrid) % (2 ^ LMUL_pow_reg) != 0 then { + /* vrid must be a multiple of emul */ + assert(false, "invalid register group: vrid is not a multiple of EMUL") + } else { + if LMUL_pow < 0 then { + result = read_single_vreg('n, SEW, vrid); + } else { + let 'num_elem_single : int = VLEN / SEW; + assert('num_elem_single >= 0); + foreach (i_lmul from 0 to (2 ^ LMUL_pow_reg - 1)) { + let r_start_i : int = i_lmul * 'num_elem_single; + let r_end_i : int = r_start_i + 'num_elem_single - 1; + let vrid_lmul : regidx = vrid + to_bits(5, i_lmul); + let single_result : vector('num_elem_single, dec, bits('m)) = read_single_vreg('num_elem_single, SEW, vrid_lmul); + foreach (r_i from r_start_i to r_end_i) { + let s_i : int = r_i - r_start_i; + assert(0 <= r_i & r_i < num_elem); + assert(0 <= s_i & s_i < 'num_elem_single); + result[r_i] = single_result[s_i]; + } + } + } + }; + + result +} + +/* Single element reading operation */ +val read_single_element : forall 'm 'x, 8 <= 'm <= 128. (int('m), int('x), regidx) -> bits('m) effect {escape, rreg, undef} +function read_single_element(EEW, index, vrid) = { + let VLEN = unsigned(vlenb) * 8; + assert(VLEN >= EEW); + let 'elem_per_reg : int = VLEN / EEW; + assert('elem_per_reg >= 0); + let real_vrid : regidx = vrid + to_bits(5, index / 'elem_per_reg); + let real_index : int = index % 'elem_per_reg; + let vrid_val : vector('elem_per_reg, dec, bits('m)) = read_single_vreg('elem_per_reg, EEW, real_vrid); + assert(0 <= real_index & real_index < 'elem_per_reg); + vrid_val[real_index] +} + +/* The general vreg writing operation with num_elem as max(VLMAX,VLEN/SEW)) */ +val write_vreg : forall 'n 'm 'p, 'n >= 0. (int('n), int('m), int('p), regidx, vector('n, dec, bits('m))) -> unit effect {escape, rreg, undef, wreg} +function write_vreg(num_elem, SEW, LMUL_pow, vrid, vec) = { + let VLEN = unsigned(vlenb) * 8; + let LMUL_pow_reg = if LMUL_pow < 0 then 0 else LMUL_pow; + + let 'num_elem_single : int = VLEN / SEW; + assert('num_elem_single >= 0); + foreach (i_lmul from 0 to (2 ^ LMUL_pow_reg - 1)) { + var single_vec : vector('num_elem_single, dec, bits('m)) = undefined; + let vrid_lmul : regidx = vrid + to_bits(5, i_lmul); + let r_start_i : int = i_lmul * 'num_elem_single; + let r_end_i : int = r_start_i + 'num_elem_single - 1; + foreach (r_i from r_start_i to r_end_i) { + let s_i : int = r_i - r_start_i; + assert(0 <= r_i & r_i < num_elem); + assert(0 <= s_i & s_i < 'num_elem_single); + single_vec[s_i] = vec[r_i] + }; + write_single_vreg('num_elem_single, SEW, vrid_lmul, single_vec) + } +} + +/* Single element writing operation */ +val write_single_element : forall 'm 'x, 8 <= 'm <= 128. (int('m), int('x), regidx, bits('m)) -> unit effect {escape, rreg, undef, wreg} +function write_single_element(EEW, index, vrid, value) = { + let VLEN = unsigned(vlenb) * 8; + let 'elem_per_reg : int = VLEN / EEW; + assert('elem_per_reg >= 0); + let real_vrid : regidx = vrid + to_bits(5, index / 'elem_per_reg); + let real_index : int = index % 'elem_per_reg; + + let vrid_val : vector('elem_per_reg, dec, bits('m)) = read_single_vreg('elem_per_reg, EEW, real_vrid); + r : vregtype = zeros(); + foreach (i from ('elem_per_reg - 1) downto 0) { + r = r << EEW; + if i == real_index then { + r = r | zero_extend(value); + } else { + r = r | zero_extend(vrid_val[i]); + } + }; + V(real_vrid) = r; +} + +/* Mask register reading operation with num_elem as max(VLMAX,VLEN/SEW)) */ +val read_vmask : forall 'n, 'n >= 0. (int('n), bits(1), regidx) -> vector('n, dec, bool) effect {escape, rreg, undef} +function read_vmask(num_elem, vm, vrid) = { + let VLEN = unsigned(vlenb) * 8; + assert(num_elem <= sizeof(vlenmax)); + let vreg_val : vregtype = V(vrid); + var result : vector('n, dec, bool) = undefined; + + foreach (i from 0 to (num_elem - 1)) { + if vm == 0b1 then { + result[i] = true + } else { + result[i] = bit_to_bool(vreg_val[i]) + } + }; + + result +} + +/* This is a special version of read_vmask for carry/borrow instructions, where vm=1 means no carry */ +val read_vmask_carry : forall 'n, 'n >= 0. (int('n), bits(1), regidx) -> vector('n, dec, bool) effect {escape, rreg, undef} +function read_vmask_carry(num_elem, vm, vrid) = { + let VLEN = unsigned(vlenb) * 8; + assert(0 < num_elem & num_elem <= sizeof(vlenmax)); + let vreg_val : vregtype = V(vrid); + var result : vector('n, dec, bool) = undefined; + + foreach (i from 0 to (num_elem - 1)) { + if vm == 0b1 then { + result[i] = false + } else { + result[i] = bit_to_bool(vreg_val[i]) + } + }; + + result +} + +/* Mask register writing operation with num_elem as max(VLMAX,VLEN/SEW)) */ +val write_vmask : forall 'n, 'n >= 0. (int('n), regidx, vector('n, dec, bool)) -> unit effect {escape, rreg, undef, wreg} +function write_vmask(num_elem, vrid, v) = { + let VLEN = unsigned(vlenb) * 8; + assert(0 < VLEN & VLEN <= sizeof(vlenmax)); + assert(0 < num_elem & num_elem <= VLEN); + let vreg_val : vregtype = V(vrid); + var result : vregtype = undefined; + + foreach (i from 0 to (num_elem - 1)) { + result[i] = bool_to_bit(v[i]) + }; + foreach (i from num_elem to (VLEN - 1)) { + /* Mask tail is always agnostic */ + result[i] = vreg_val[i] /* TODO: configuration support */ + }; + + V(vrid) = result +} + +/* end vector register */ diff --git a/model/riscv_vlen.sail b/model/riscv_vlen.sail new file mode 100644 index 000000000..5e9b37542 --- /dev/null +++ b/model/riscv_vlen.sail @@ -0,0 +1,83 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +register elen : bits(1) + +val get_elen_pow : unit -> {|5, 6|} effect {rreg} + +function get_elen_pow() = match elen { + 0b0 => 5, + 0b1 => 6 +} +/* Note: ELEN=32 requires a different encoding of the CSR vtype. + * The current version of vtype implementation corresponds to the ELEN=64 configuration. + * TODO: the configurarion of ELEN and its corresponding vtype implementations. + */ + +register vlen : bits(4) + +val get_vlen_pow : unit -> {|5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16|} effect {rreg} + +function get_vlen_pow() = match vlen { + 0b0000 => 5, + 0b0001 => 6, + 0b0010 => 7, + 0b0011 => 8, + 0b0100 => 9, + 0b0101 => 10, + 0b0110 => 11, + 0b0111 => 12, + 0b1000 => 13, + 0b1001 => 14, + 0b1010 => 15, + _ => 16 +} + +type vlenmax : Int = 65536 + +/* Note: At present, the values of elen and vlen need to be manually speficied + * in the init_sys() function of riscv_sys_control.sail before compiling the emulators, + * e.g., + * vlen = 0b0101; + * elen = 0b1; + * means VLEN = 1024 and ELEN = 64, + * They will be configurable when user-specified configuration is supported in Sail. + * + * Also, VLEN >= ELEN must be satisfied and this condition check should be added + * after their initialization. + */ diff --git a/model/riscv_vreg_type.sail b/model/riscv_vreg_type.sail new file mode 100755 index 000000000..6f553757b --- /dev/null +++ b/model/riscv_vreg_type.sail @@ -0,0 +1,179 @@ +/*=================================================================================*/ +/* Copyright (c) 2021-2023 */ +/* Authors from RIOS Lab, Tsinghua University: */ +/* Xinlai Wan */ +/* Xi Wang */ +/* Yifei Zhu */ +/* Shenwei Hu */ +/* Kalvin Vu */ +/* Other contributors: */ +/* Jessica Clarke */ +/* Victor Moya */ +/* */ +/* All rights reserved. */ +/* */ +/* Redistribution and use in source and binary forms, with or without */ +/* modification, are permitted provided that the following conditions */ +/* are met: */ +/* 1. Redistributions of source code must retain the above copyright */ +/* notice, this list of conditions and the following disclaimer. */ +/* 2. Redistributions in binary form must reproduce the above copyright */ +/* notice, this list of conditions and the following disclaimer in */ +/* the documentation and/or other materials provided with the */ +/* distribution. */ +/* */ +/* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' */ +/* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED */ +/* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A */ +/* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR */ +/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */ +/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */ +/* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF */ +/* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND */ +/* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, */ +/* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT */ +/* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF */ +/* SUCH DAMAGE. */ +/*=================================================================================*/ + +/* Definitions for vector registers (V extension) */ + +type vreglenbits = bits(vlenmax) /* use the largest possible register length */ + +/* default vector register type */ +type vregtype = vreglenbits + +/* vector instruction types */ +enum vsetop = { VSETVLI, VSETVL } + +enum vvfunct6 = { VV_VADD, VV_VSUB, VV_VMINU, VV_VMIN, VV_VMAXU, VV_VMAX, VV_VAND, VV_VOR, VV_VXOR, + VV_VRGATHER, VV_VRGATHEREI16, VV_VSADDU, VV_VSADD, VV_VSSUBU, VV_VSSUB, VV_VSLL, VV_VSMUL, + VV_VSRL, VV_VSRA, VV_VSSRL, VV_VSSRA } + +enum vvcmpfunct6 = { VVCMP_VMSEQ, VVCMP_VMSNE, VVCMP_VMSLTU, VVCMP_VMSLT, VVCMP_VMSLEU, VVCMP_VMSLE } + +enum vvmfunct6 = { VVM_VMADC, VVM_VMSBC } + +enum vvmcfunct6 = { VVMC_VMADC, VVMC_VMSBC } + +enum vvmsfunct6 = { VVMS_VADC, VVMS_VSBC } + +enum vxmfunct6 = { VXM_VMADC, VXM_VMSBC } + +enum vxmcfunct6 = { VXMC_VMADC, VXMC_VMSBC } + +enum vxmsfunct6 = { VXMS_VADC, VXMS_VSBC } + +enum vimfunct6 = { VIM_VMADC } + +enum vimcfunct6 = { VIMC_VMADC } + +enum vimsfunct6 = { VIMS_VADC } + +enum vxcmpfunct6 = { VXCMP_VMSEQ, VXCMP_VMSNE, VXCMP_VMSLTU, VXCMP_VMSLT, VXCMP_VMSLEU, VXCMP_VMSLE, + VXCMP_VMSGTU, VXCMP_VMSGT } + +enum vicmpfunct6 = { VICMP_VMSEQ, VICMP_VMSNE, VICMP_VMSLEU, VICMP_VMSLE, VICMP_VMSGTU, VICMP_VMSGT } + +enum nvfunct6 = { NV_VNCLIPU, NV_VNCLIP } + +enum nvsfunct6 = { NVS_VNSRL, NVS_VNSRA } + +enum nxfunct6 = { NX_VNCLIPU, NX_VNCLIP} + +enum nxsfunct6 = { NXS_VNSRL, NXS_VNSRA } + +enum mmfunct6 = { MM_VMAND, MM_VMNAND, MM_VMANDNOT, MM_VMXOR, MM_VMOR, MM_VMNOR, MM_VMORNOT, MM_VMXNOR } + +enum nifunct6 = { NI_VNCLIPU, NI_VNCLIP } + +enum nisfunct6 = { NIS_VNSRL, NIS_VNSRA } + +enum wvvfunct6 = { WVV_VADD, WVV_VSUB, WVV_VADDU, WVV_VSUBU, WVV_VWMUL, WVV_VWMULU, WVV_VWMULSU } + +enum wvfunct6 = { WV_VADD, WV_VSUB, WV_VADDU, WV_VSUBU } + +enum wvxfunct6 = { WVX_VADD, WVX_VSUB, WVX_VADDU, WVX_VSUBU, WVX_VWMUL, WVX_VWMULU, WVX_VWMULSU } + +enum wxfunct6 = { WX_VADD, WX_VSUB, WX_VADDU, WX_VSUBU } + +enum vext2funct6 = { VEXT2_ZVF2, VEXT2_SVF2 } + +enum vext4funct6 = { VEXT4_ZVF4, VEXT4_SVF4 } + +enum vext8funct6 = { VEXT8_ZVF8, VEXT8_SVF8 } + +enum vxfunct6 = { VX_VADD, VX_VSUB, VX_VRSUB, VX_VMINU, VX_VMIN, VX_VMAXU, VX_VMAX, + VX_VAND, VX_VOR, VX_VXOR, VX_VSADDU, VX_VSADD, VX_VSSUBU, VX_VSSUB, + VX_VSLL, VX_VSMUL, VX_VSRL, VX_VSRA, VX_VSSRL, VX_VSSRA } + +enum vifunct6 = { VI_VADD, VI_VRSUB, VI_VAND, VI_VOR, VI_VXOR, VI_VSADDU, VI_VSADD, + VI_VSLL, VI_VSRL, VI_VSRA, VI_VSSRL, VI_VSSRA } + +enum vxsgfunct6 = { VX_VSLIDEUP, VX_VSLIDEDOWN, VX_VRGATHER } + +enum visgfunct6 = { VI_VSLIDEUP, VI_VSLIDEDOWN, VI_VRGATHER } + +enum mvvfunct6 = { MVV_VAADDU, MVV_VAADD, MVV_VASUBU, MVV_VASUB, MVV_VMUL, MVV_VMULH, + MVV_VMULHU, MVV_VMULHSU, MVV_VDIVU, MVV_VDIV, MVV_VREMU, MVV_VREM } + +enum mvvmafunct6 = { MVV_VMACC, MVV_VNMSAC, MVV_VMADD, MVV_VNMSUB } + +enum rmvvfunct6 = { MVV_VREDSUM, MVV_VREDAND, MVV_VREDOR, MVV_VREDXOR, + MVV_VREDMINU, MVV_VREDMIN, MVV_VREDMAXU, MVV_VREDMAX } + +enum rivvfunct6 = { IVV_VWREDSUMU, IVV_VWREDSUM } + +enum rfvvfunct6 = { FVV_VFREDOSUM, FVV_VFREDUSUM, FVV_VFREDMAX, FVV_VFREDMIN, + FVV_VFWREDOSUM, FVV_VFWREDUSUM } + +enum wmvvfunct6 = { WMVV_VWMACCU, WMVV_VWMACC, WMVV_VWMACCSU } + +enum mvxfunct6 = { MVX_VAADDU, MVX_VAADD, MVX_VASUBU, MVX_VASUB, MVX_VSLIDE1UP, MVX_VSLIDE1DOWN, + MVX_VMUL, MVX_VMULH, MVX_VMULHU, MVX_VMULHSU, MVX_VDIVU, MVX_VDIV, MVX_VREMU, MVX_VREM } + +enum mvxmafunct6 = { MVX_VMACC, MVX_VNMSAC, MVX_VMADD, MVX_VNMSUB } + +enum wmvxfunct6 = { WMVX_VWMACCU, WMVX_VWMACC, WMVX_VWMACCUS, WMVX_VWMACCSU } + +enum maskfunct3 = { VV_VMERGE, VI_VMERGE, VX_VMERGE } + +enum vlewidth = { VLE8, VLE16, VLE32, VLE64 } + +enum fvvfunct6 = { FVV_VADD, FVV_VSUB, FVV_VMIN, FVV_VMAX, FVV_VSGNJ, FVV_VSGNJN, FVV_VSGNJX, + FVV_VDIV, FVV_VMUL } + +enum fvvmafunct6 = { FVV_VMADD, FVV_VNMADD, FVV_VMSUB, FVV_VNMSUB, FVV_VMACC, FVV_VNMACC, FVV_VMSAC, FVV_VNMSAC } + +enum fwvvfunct6 = { FWVV_VADD, FWVV_VSUB, FWVV_VMUL } + +enum fwvvmafunct6 = { FWVV_VMACC, FWVV_VNMACC, FWVV_VMSAC, FWVV_VNMSAC } + +enum fwvfunct6 = { FWV_VADD, FWV_VSUB } + +enum fvvmfunct6 = { FVVM_VMFEQ, FVVM_VMFLE, FVVM_VMFLT, FVVM_VMFNE } + +enum vfunary0 = { FV_CVT_XU_F, FV_CVT_X_F, FV_CVT_F_XU, FV_CVT_F_X, FV_CVT_RTZ_XU_F, FV_CVT_RTZ_X_F } + +enum vfwunary0 = { FWV_CVT_XU_F, FWV_CVT_X_F, FWV_CVT_F_XU, FWV_CVT_F_X, FWV_CVT_F_F, + FWV_CVT_RTZ_XU_F, FWV_CVT_RTZ_X_F } + +enum vfnunary0 = { FNV_CVT_XU_F, FNV_CVT_X_F, FNV_CVT_F_XU, FNV_CVT_F_X, FNV_CVT_F_F, + FNV_CVT_ROD_F_F, FNV_CVT_RTZ_XU_F, FNV_CVT_RTZ_X_F} + +enum vfunary1 = { FVV_VSQRT, FVV_VRSQRT7, FVV_VREC7, FVV_VCLASS } + +enum fvffunct6 = { VF_VADD, VF_VSUB, VF_VMIN, VF_VMAX, VF_VSGNJ, VF_VSGNJN, VF_VSGNJX, + VF_VDIV, VF_VRDIV, VF_VMUL, VF_VRSUB, VF_VSLIDE1UP, VF_VSLIDE1DOWN } + +enum fvfmafunct6 = { VF_VMADD, VF_VNMADD, VF_VMSUB, VF_VNMSUB, VF_VMACC, VF_VNMACC, VF_VMSAC, VF_VNMSAC } + +enum fwvffunct6 = { FWVF_VADD, FWVF_VSUB, FWVF_VMUL } + +enum fwvfmafunct6 = { FWVF_VMACC, FWVF_VNMACC, FWVF_VMSAC, FWVF_VNMSAC } + +enum fwffunct6 = { FWF_VADD, FWF_VSUB } + +enum fvfmfunct6 = { VFM_VMFEQ, VFM_VMFLE, VFM_VMFLT, VFM_VMFNE, VFM_VMFGT, VFM_VMFGE } + +enum vmlsop = { VLM, VSM } diff --git a/ocaml_emulator/platform.ml b/ocaml_emulator/platform.ml index ccf487589..bfd072ef5 100644 --- a/ocaml_emulator/platform.ml +++ b/ocaml_emulator/platform.ml @@ -11,6 +11,7 @@ let config_enable_dirty_update = ref false let config_enable_misaligned_access = ref false let config_mtval_has_illegal_inst_bits = ref false let config_enable_pmp = ref false +let config_enable_vext = ref true let platform_arch = ref P.RV64 @@ -78,6 +79,7 @@ let enable_writable_misa () = !config_enable_writable_misa let enable_rvc () = !config_enable_rvc let enable_next () = !config_enable_next let enable_fdext () = false +let enable_vext () = !config_enable_vext let enable_dirty_update () = !config_enable_dirty_update let enable_misaligned_access () = !config_enable_misaligned_access let mtval_has_illegal_inst_bits () = !config_mtval_has_illegal_inst_bits diff --git a/ocaml_emulator/riscv_ocaml_sim.ml b/ocaml_emulator/riscv_ocaml_sim.ml index 6e612ad26..9740eb58b 100644 --- a/ocaml_emulator/riscv_ocaml_sim.ml +++ b/ocaml_emulator/riscv_ocaml_sim.ml @@ -53,6 +53,9 @@ let options = Arg.align ([("-dump-dts", ("-disable-rvc", Arg.Clear P.config_enable_rvc, " disable the RVC extension on boot"); + ("-disable-vext", + Arg.Clear P.config_enable_vext, + " disable the RVV extension on boot"); ("-disable-writable-misa-c", Arg.Clear P.config_enable_writable_misa, " leave misa hardwired to its initial value");