From c90cf2e6eff5fa4ef7b93cc2020166dea7453fc6 Mon Sep 17 00:00:00 2001 From: Xinlai Wan Date: Tue, 27 Dec 2022 20:23:10 +0800 Subject: [PATCH] RISC-V Vector Extension Support This PR adds the following: General Framework and Configurations: * Introduced the V extension's general framework and configuration setting instructions. * Updated model/riscv_insts_vext_vset.sail and effect matching functions in riscv_vlen.sail. * Addressed code formatting issues and made revisions post the Nov 22 meeting. * Co-authored by Nicolas Brunie and Jessica Clarke. Vector Load/Store Instructions: * Integrated vector load and store instructions. * Enhanced the implementation of SEW, LMUL, VLEN and removed real numbers from the code. * Updated vstart settings and removed unnecessary assert statements. * Rectified bugs in vleff instructions and overhauled coding styles. * Incorporated guards for vector encdec clauses and optimized memory access post vector load/store failures. Vector Integer/Fixed-Point Instructions: * Added vector integer/fixed-point arithmetic and mask instructions. * Improved vector EEW and EMUL checking functions and introduced illegal instruction check functions. * Fine-tuned code formatting for vector instruction checks. Vector Floating-Point Instructions: * Rolled out vector floating-point instructions and updated their conversion counterparts. * Refreshed copyright headers specific to the vector extension code. Vector Reduction and Mask Instructions: * Integrated vector mask and reduction instructions. * Addressed register overlap checks for vector mask instructions. Miscellaneous Enhancements and Fixes: * Updated vector CSR vtype.vill settings and judgements. * Systematized patterns for vector illegal instruction checks. * Rectified issues in vector load/store and reduction operations. * Purged redundant elements from the V extension code and vector floating-point functions. * Cleaned up softfloat makefiles and renamed EXTZ and EXTS within the V extension code. * Addressed a clang-format check issue and NaN boxing anomalies. Provided annotations for pending RVV configurations. * Initialized default VLEN value and set vlenb CSR. * Set constraints for vector variable initialization and added mstatus.VS settings specific to the 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 | 26 +- 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 | 32 +- model/riscv_sys_regs.sail | 98 +- 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, 8547 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 f2dfab4af..fbd63fa88 100644 --- a/c_emulator/riscv_platform.c +++ b/c_emulator/riscv_platform.c @@ -42,6 +42,11 @@ bool sys_enable_writable_fiom(unit u) return rv_enable_writable_fiom; } +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 4442f9503..4b6541f9c 100644 --- a/c_emulator/riscv_platform.h +++ b/c_emulator/riscv_platform.h @@ -7,6 +7,7 @@ bool sys_enable_fdext(unit); bool sys_enable_zfinx(unit); bool sys_enable_writable_misa(unit); bool sys_enable_writable_fiom(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 34406cae5..15ff8adf9 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 c74cda739..e5c562af3 100644 --- a/c_emulator/riscv_platform_impl.h +++ b/c_emulator/riscv_platform_impl.h @@ -13,6 +13,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 276be0cbe..919451857 100644 --- a/c_emulator/riscv_sim.c +++ b/c_emulator/riscv_sim.c @@ -246,6 +246,7 @@ static int process_args(int argc, char **argv) "N" "I" "F" + "W" "i" "s" "p" @@ -300,6 +301,10 @@ static int 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 bec76d645..6b5e46c5d 100644 --- a/model/prelude.sail +++ b/model/prelude.sail @@ -198,8 +198,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 { @@ -327,3 +330,24 @@ 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) + +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 e3c1c20b9..da68556f0 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..3e29854a6 --- /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 425f7a3dc..8953ad4b3 100644 --- a/model/riscv_insts_zicsr.sail +++ b/model/riscv_insts_zicsr.sail @@ -137,6 +137,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 */ @@ -250,6 +259,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 b98c44465..047fddb89 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) function riscv_f32ToI32 (rm, v) = { diff --git a/model/riscv_sys_control.sail b/model/riscv_sys_control.sail index cfac8bda9..3830725d1 100644 --- a/model/riscv_sys_control.sail +++ b/model/riscv_sys_control.sail @@ -556,12 +556,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!"); @@ -602,6 +603,25 @@ function init_sys() -> unit = { menvcfg->bits() = zero_extend(0b0); senvcfg->bits() = zero_extend(0b0); + /* 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(); diff --git a/model/riscv_sys_regs.sail b/model/riscv_sys_regs.sail index 81a6c7664..f472ca2b8 100644 --- a/model/riscv_sys_regs.sail +++ b/model/riscv_sys_regs.sail @@ -153,6 +153,8 @@ val sys_enable_next = {c: "sys_enable_next", ocaml: "Platform.enable_next", _: " /* Whether FIOM bit of menvcfg/senvcfg is enabled. It must be enabled if supervisor mode is implemented and non-bare addressing modes are supported. */ val sys_enable_writable_fiom = {c: "sys_enable_writable_fiom", ocaml: "Platform.enable_writable_fiom", _: "sys_enable_writable_fiom"} : 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()); @@ -872,3 +882,85 @@ function is_fiom_active() -> bool = { User => (menvcfg.FIOM() | senvcfg.FIOM()) == 0b1, } } +/* 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 1e611657a..e4dbfeb4d 100644 --- a/ocaml_emulator/platform.ml +++ b/ocaml_emulator/platform.ml @@ -12,6 +12,7 @@ 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_writable_fiom = ref true +let config_enable_vext = ref true let platform_arch = ref P.RV64 @@ -79,6 +80,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 814f887b9..c151d69af 100644 --- a/ocaml_emulator/riscv_ocaml_sim.ml +++ b/ocaml_emulator/riscv_ocaml_sim.ml @@ -56,6 +56,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");