mirror of
https://github.com/qemu/qemu.git
synced 2024-11-28 06:13:46 +08:00
Third RISC-V PR for 8.1
* Use xl instead of mxl for disassemble * Factor out extension tests to cpu_cfg.h * disas/riscv: Add vendor extension support * disas/riscv: Add support for XVentanaCondOps * disas/riscv: Add support for XThead* instructions * Fix mstatus related problems * Fix veyron-v1 CPU properties * Fix the xlen for data address when MPRV=1 * opensbi: Upgrade from v1.2 to v1.3 * Enable 32-bit Spike OpenSBI boot testing * Support the watchdog timer of HiFive 1 rev b * Only build qemu-system-riscv$$ on rv$$ host * Add RVV registers to log * Restrict ACLINT to TCG * Add syscall riscv_hwprobe * Add support for BF16 extensions * KVM_RISCV_SET_TIMER macro is not configured correctly * Generate devicetree only after machine initialization is complete * virt: Convert fdt_load_addr to uint64_t * KVM: fixes and enhancements * Add support for the Zfa extension -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmSr+ekACgkQr3yVEwxT gBMMGg//ZCcyH3KXB49c2KUIFO6FKYUxN9uC3giZCtuGyEH8T2yDgZVVXnxwU+Ij +3Ej6T/ZdWMpePC9qf+xKzHWZk7Qc8Tcg+JgQbga573894yZInRwYl8HsSlEKA+Z vlqSBPxTlp9rlDwGP/LjGljyIFqL4konk9zi3FL4ZXTF1iHUGrh/953Y3wIreEfl KX5UznnWcgy2BqQT1vihMbM8qCVK6iryH+QZ6LiAsPMSX1rIzk8ectQryILzoIYh bMiwCLVMyr4ZrUXjmGTF+7/WcOWwhhyfpdstf2iotKALelZtVHit0wHcty2GYQde nvN83jJWu04DGXkPBUsqCUQXczGo1QHjJUH3RIRJzfOby/lGt4pSzHAfKA+iNUht ikM3SdBsXMO+ogjTtTcCMb7/m2vsMoQP60VRts9Mh3YVD0cgr7RqpqRoEMugVYnr ca8Vijf71mB+y+pq477eV1Q8BoKpr8xa1OlFkNKPC17uMD7HoDMI44QgFOgtYp10 TMsqqyB75q6PZhSEwm63xbmH0Zpo8kSqT/E3MTtGTyPeuL8TNNNSkCmFaGYmRrbI XEp7vG2RaDJOvDomS3nUhA5ruc8SaXd0q25q2gLYQfCsehfFqZAwuNB5xf1zS0M0 ov1/gwaqU93t6nLbo2cCbb0plkIFKwwJ9KKjD06wJ4KPe0TGFzk= =3XFD -----END PGP SIGNATURE----- Merge tag 'pull-riscv-to-apply-20230710-1' of https://github.com/alistair23/qemu into staging Third RISC-V PR for 8.1 * Use xl instead of mxl for disassemble * Factor out extension tests to cpu_cfg.h * disas/riscv: Add vendor extension support * disas/riscv: Add support for XVentanaCondOps * disas/riscv: Add support for XThead* instructions * Fix mstatus related problems * Fix veyron-v1 CPU properties * Fix the xlen for data address when MPRV=1 * opensbi: Upgrade from v1.2 to v1.3 * Enable 32-bit Spike OpenSBI boot testing * Support the watchdog timer of HiFive 1 rev b * Only build qemu-system-riscv$$ on rv$$ host * Add RVV registers to log * Restrict ACLINT to TCG * Add syscall riscv_hwprobe * Add support for BF16 extensions * KVM_RISCV_SET_TIMER macro is not configured correctly * Generate devicetree only after machine initialization is complete * virt: Convert fdt_load_addr to uint64_t * KVM: fixes and enhancements * Add support for the Zfa extension # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEEaukCtqfKh31tZZKWr3yVEwxTgBMFAmSr+ekACgkQr3yVEwxT # gBMMGg//ZCcyH3KXB49c2KUIFO6FKYUxN9uC3giZCtuGyEH8T2yDgZVVXnxwU+Ij # +3Ej6T/ZdWMpePC9qf+xKzHWZk7Qc8Tcg+JgQbga573894yZInRwYl8HsSlEKA+Z # vlqSBPxTlp9rlDwGP/LjGljyIFqL4konk9zi3FL4ZXTF1iHUGrh/953Y3wIreEfl # KX5UznnWcgy2BqQT1vihMbM8qCVK6iryH+QZ6LiAsPMSX1rIzk8ectQryILzoIYh # bMiwCLVMyr4ZrUXjmGTF+7/WcOWwhhyfpdstf2iotKALelZtVHit0wHcty2GYQde # nvN83jJWu04DGXkPBUsqCUQXczGo1QHjJUH3RIRJzfOby/lGt4pSzHAfKA+iNUht # ikM3SdBsXMO+ogjTtTcCMb7/m2vsMoQP60VRts9Mh3YVD0cgr7RqpqRoEMugVYnr # ca8Vijf71mB+y+pq477eV1Q8BoKpr8xa1OlFkNKPC17uMD7HoDMI44QgFOgtYp10 # TMsqqyB75q6PZhSEwm63xbmH0Zpo8kSqT/E3MTtGTyPeuL8TNNNSkCmFaGYmRrbI # XEp7vG2RaDJOvDomS3nUhA5ruc8SaXd0q25q2gLYQfCsehfFqZAwuNB5xf1zS0M0 # ov1/gwaqU93t6nLbo2cCbb0plkIFKwwJ9KKjD06wJ4KPe0TGFzk= # =3XFD # -----END PGP SIGNATURE----- # gpg: Signature made Mon 10 Jul 2023 01:30:33 PM BST # gpg: using RSA key 6AE902B6A7CA877D6D659296AF7C95130C538013 # gpg: Good signature from "Alistair Francis <alistair@alistair23.me>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 6AE9 02B6 A7CA 877D 6D65 9296 AF7C 9513 0C53 8013 * tag 'pull-riscv-to-apply-20230710-1' of https://github.com/alistair23/qemu: (54 commits) riscv: Add support for the Zfa extension target/riscv/kvm.c: read/write (cbom|cboz)_blocksize in KVM target/riscv/kvm.c: add kvmconfig_get_cfg_addr() helper target/riscv: update multi-letter extension KVM properties target/riscv/cpu.c: create KVM mock properties target/riscv/cpu.c: remove priv_ver check from riscv_isa_string_ext() target/riscv/cpu.c: add satp_mode properties earlier target/riscv/kvm.c: add multi-letter extension KVM properties target/riscv/kvm.c: update KVM MISA bits target/riscv: add KVM specific MISA properties target/riscv/cpu: add misa_ext_info_arr[] target/riscv/kvm.c: init 'misa_ext_mask' with scratch CPU target/riscv: handle mvendorid/marchid/mimpid for KVM CPUs target/riscv: read marchid/mimpid in kvm_riscv_init_machine_ids() target/riscv: use KVM scratch CPUs to init KVM properties target/riscv/cpu.c: restrict 'marchid' value target/riscv/cpu.c: restrict 'mimpid' value target/riscv/cpu.c: restrict 'mvendorid' value hw/riscv/virt.c: skip 'mmu-type' FDT if satp mode not set target/riscv: skip features setup for KVM CPUs ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
94d68c1136
@ -6,7 +6,11 @@ common_ss.add(when: 'CONFIG_M68K_DIS', if_true: files('m68k.c'))
|
||||
common_ss.add(when: 'CONFIG_MICROBLAZE_DIS', if_true: files('microblaze.c'))
|
||||
common_ss.add(when: 'CONFIG_MIPS_DIS', if_true: files('mips.c', 'nanomips.c'))
|
||||
common_ss.add(when: 'CONFIG_NIOS2_DIS', if_true: files('nios2.c'))
|
||||
common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files('riscv.c'))
|
||||
common_ss.add(when: 'CONFIG_RISCV_DIS', if_true: files(
|
||||
'riscv.c',
|
||||
'riscv-xthead.c',
|
||||
'riscv-xventana.c'
|
||||
))
|
||||
common_ss.add(when: 'CONFIG_SH4_DIS', if_true: files('sh4.c'))
|
||||
common_ss.add(when: 'CONFIG_SPARC_DIS', if_true: files('sparc.c'))
|
||||
common_ss.add(when: 'CONFIG_XTENSA_DIS', if_true: files('xtensa.c'))
|
||||
|
707
disas/riscv-xthead.c
Normal file
707
disas/riscv-xthead.c
Normal file
@ -0,0 +1,707 @@
|
||||
/*
|
||||
* QEMU RISC-V Disassembler for xthead.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "disas/riscv.h"
|
||||
#include "disas/riscv-xthead.h"
|
||||
|
||||
typedef enum {
|
||||
/* 0 is reserved for rv_op_illegal. */
|
||||
/* XTheadBa */
|
||||
rv_op_th_addsl = 1,
|
||||
/* XTheadBb */
|
||||
rv_op_th_srri,
|
||||
rv_op_th_srriw,
|
||||
rv_op_th_ext,
|
||||
rv_op_th_extu,
|
||||
rv_op_th_ff0,
|
||||
rv_op_th_ff1,
|
||||
rv_op_th_rev,
|
||||
rv_op_th_revw,
|
||||
rv_op_th_tstnbz,
|
||||
/* XTheadBs */
|
||||
rv_op_th_tst,
|
||||
/* XTheadCmo */
|
||||
rv_op_th_dcache_call,
|
||||
rv_op_th_dcache_ciall,
|
||||
rv_op_th_dcache_iall,
|
||||
rv_op_th_dcache_cpa,
|
||||
rv_op_th_dcache_cipa,
|
||||
rv_op_th_dcache_ipa,
|
||||
rv_op_th_dcache_cva,
|
||||
rv_op_th_dcache_civa,
|
||||
rv_op_th_dcache_iva,
|
||||
rv_op_th_dcache_csw,
|
||||
rv_op_th_dcache_cisw,
|
||||
rv_op_th_dcache_isw,
|
||||
rv_op_th_dcache_cpal1,
|
||||
rv_op_th_dcache_cval1,
|
||||
rv_op_th_icache_iall,
|
||||
rv_op_th_icache_ialls,
|
||||
rv_op_th_icache_ipa,
|
||||
rv_op_th_icache_iva,
|
||||
rv_op_th_l2cache_call,
|
||||
rv_op_th_l2cache_ciall,
|
||||
rv_op_th_l2cache_iall,
|
||||
/* XTheadCondMov */
|
||||
rv_op_th_mveqz,
|
||||
rv_op_th_mvnez,
|
||||
/* XTheadFMemIdx */
|
||||
rv_op_th_flrd,
|
||||
rv_op_th_flrw,
|
||||
rv_op_th_flurd,
|
||||
rv_op_th_flurw,
|
||||
rv_op_th_fsrd,
|
||||
rv_op_th_fsrw,
|
||||
rv_op_th_fsurd,
|
||||
rv_op_th_fsurw,
|
||||
/* XTheadFmv */
|
||||
rv_op_th_fmv_hw_x,
|
||||
rv_op_th_fmv_x_hw,
|
||||
/* XTheadMac */
|
||||
rv_op_th_mula,
|
||||
rv_op_th_mulah,
|
||||
rv_op_th_mulaw,
|
||||
rv_op_th_muls,
|
||||
rv_op_th_mulsw,
|
||||
rv_op_th_mulsh,
|
||||
/* XTheadMemIdx */
|
||||
rv_op_th_lbia,
|
||||
rv_op_th_lbib,
|
||||
rv_op_th_lbuia,
|
||||
rv_op_th_lbuib,
|
||||
rv_op_th_lhia,
|
||||
rv_op_th_lhib,
|
||||
rv_op_th_lhuia,
|
||||
rv_op_th_lhuib,
|
||||
rv_op_th_lwia,
|
||||
rv_op_th_lwib,
|
||||
rv_op_th_lwuia,
|
||||
rv_op_th_lwuib,
|
||||
rv_op_th_ldia,
|
||||
rv_op_th_ldib,
|
||||
rv_op_th_sbia,
|
||||
rv_op_th_sbib,
|
||||
rv_op_th_shia,
|
||||
rv_op_th_shib,
|
||||
rv_op_th_swia,
|
||||
rv_op_th_swib,
|
||||
rv_op_th_sdia,
|
||||
rv_op_th_sdib,
|
||||
rv_op_th_lrb,
|
||||
rv_op_th_lrbu,
|
||||
rv_op_th_lrh,
|
||||
rv_op_th_lrhu,
|
||||
rv_op_th_lrw,
|
||||
rv_op_th_lrwu,
|
||||
rv_op_th_lrd,
|
||||
rv_op_th_srb,
|
||||
rv_op_th_srh,
|
||||
rv_op_th_srw,
|
||||
rv_op_th_srd,
|
||||
rv_op_th_lurb,
|
||||
rv_op_th_lurbu,
|
||||
rv_op_th_lurh,
|
||||
rv_op_th_lurhu,
|
||||
rv_op_th_lurw,
|
||||
rv_op_th_lurwu,
|
||||
rv_op_th_lurd,
|
||||
rv_op_th_surb,
|
||||
rv_op_th_surh,
|
||||
rv_op_th_surw,
|
||||
rv_op_th_surd,
|
||||
/* XTheadMemPair */
|
||||
rv_op_th_ldd,
|
||||
rv_op_th_lwd,
|
||||
rv_op_th_lwud,
|
||||
rv_op_th_sdd,
|
||||
rv_op_th_swd,
|
||||
/* XTheadSync */
|
||||
rv_op_th_sfence_vmas,
|
||||
rv_op_th_sync,
|
||||
rv_op_th_sync_i,
|
||||
rv_op_th_sync_is,
|
||||
rv_op_th_sync_s,
|
||||
} rv_xthead_op;
|
||||
|
||||
const rv_opcode_data xthead_opcode_data[] = {
|
||||
{ "th.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
/* XTheadBa */
|
||||
{ "th.addsl", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
/* XTheadBb */
|
||||
{ "th.srri", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
|
||||
{ "th.srriw", rv_codec_r2_imm5, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
|
||||
{ "th.ext", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 },
|
||||
{ "th.extu", rv_codec_r2_immhl, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 },
|
||||
{ "th.ff0", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.ff1", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.rev", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.revw", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.tstnbz", rv_codec_r2, rv_fmt_rd_rs1, NULL, 0, 0, 0 },
|
||||
/* XTheadBs */
|
||||
{ "th.tst", rv_codec_r2_imm6, rv_fmt_rd_rs1_imm, NULL, 0, 0, 0 },
|
||||
/* XTheadCmo */
|
||||
{ "th.dcache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.cpa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.cipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.cva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.civa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.csw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.cisw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.isw", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.cpal1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.dcache.cval1", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.icache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.icache.ialls", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.icache.ipa", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.icache.iva", rv_codec_r, rv_fmt_rs1, NULL, 0, 0, 0 },
|
||||
{ "th.l2cache.call", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.l2cache.ciall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.l2cache.iall", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
/* XTheadCondMov */
|
||||
{ "th.mveqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "th.mvnez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
/* XTheadFMemIdx */
|
||||
{ "th.flrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.flrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.flurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.flurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.fsrd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.fsrw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.fsurd", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.fsurw", rv_codec_r_imm2, rv_fmt_frd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
/* XTheadFmv */
|
||||
{ "th.fmv.hw.x", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
|
||||
{ "th.fmv.x.hw", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
|
||||
/* XTheadMac */
|
||||
{ "th.mula", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "th.mulaw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "th.mulah", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "th.muls", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "th.mulsw", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "th.mulsh", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
/* XTheadMemIdx */
|
||||
{ "th.lbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml, NULL, 0, 0, 0 },
|
||||
{ "th.lbuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lbuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lhia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lhib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lhuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lhuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lwia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lwib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lwuia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lwuib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.ldia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.ldib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.sbia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.sbib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.shia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.shib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.swia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.swib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.sdia", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.sdib", rv_codec_r2_imm2_imm5, rv_fmt_rd_rs1_immh_imml_addr, NULL, 0, 0, 0 },
|
||||
{ "th.lrb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lrbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lrh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lrhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lrw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lrwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lrd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.srb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.srh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.srw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.srd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lurb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lurbu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lurh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lurhu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lurw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lurwu", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lurd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.surb", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.surh", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.surw", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.surd", rv_codec_r_imm2, rv_fmt_rd_rs1_rs2_imm, NULL, 0, 0, 0 },
|
||||
/* XTheadMemPair */
|
||||
{ "th.ldd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lwd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.lwud", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.sdd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
|
||||
{ "th.swd", rv_codec_r_imm2, rv_fmt_rd2_imm, NULL, 0, 0, 0 },
|
||||
/* XTheadSync */
|
||||
{ "th.sfence.vmas", rv_codec_r, rv_fmt_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "th.sync", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.sync.i", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.sync.is", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "th.sync.s", rv_codec_none, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
void decode_xtheadba(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 1:
|
||||
switch ((inst >> 25) & 0b1111111) {
|
||||
case 0b0000000:
|
||||
case 0b0000001:
|
||||
case 0b0000010:
|
||||
case 0b0000011: op = rv_op_th_addsl; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadbb(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 1:
|
||||
switch ((inst >> 25) & 0b1111111) {
|
||||
case 0b0001010: op = rv_op_th_srriw; break;
|
||||
case 0b1000000:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_tstnbz;
|
||||
}
|
||||
break;
|
||||
case 0b1000001:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_rev;
|
||||
}
|
||||
break;
|
||||
case 0b1000010:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_ff0;
|
||||
}
|
||||
break;
|
||||
case 0b1000011:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_ff1;
|
||||
}
|
||||
break;
|
||||
case 0b1000100:
|
||||
case 0b1001000:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_revw;
|
||||
}
|
||||
break;
|
||||
case 0b0000100:
|
||||
case 0b0000101: op = rv_op_th_srri; break;
|
||||
}
|
||||
break;
|
||||
case 2: op = rv_op_th_ext; break;
|
||||
case 3: op = rv_op_th_extu; break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadbs(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 1:
|
||||
switch ((inst >> 26) & 0b111111) {
|
||||
case 0b100010: op = rv_op_th_tst; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadcmo(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 0:
|
||||
switch ((inst >> 20 & 0b111111111111)) {
|
||||
case 0b000000000001:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_dcache_call;
|
||||
}
|
||||
break;
|
||||
case 0b000000000011:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_dcache_ciall;
|
||||
}
|
||||
break;
|
||||
case 0b000000000010:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_dcache_iall;
|
||||
}
|
||||
break;
|
||||
case 0b000000101001: op = rv_op_th_dcache_cpa; break;
|
||||
case 0b000000101011: op = rv_op_th_dcache_cipa; break;
|
||||
case 0b000000101010: op = rv_op_th_dcache_ipa; break;
|
||||
case 0b000000100101: op = rv_op_th_dcache_cva; break;
|
||||
case 0b000000100111: op = rv_op_th_dcache_civa; break;
|
||||
case 0b000000100110: op = rv_op_th_dcache_iva; break;
|
||||
case 0b000000100001: op = rv_op_th_dcache_csw; break;
|
||||
case 0b000000100011: op = rv_op_th_dcache_cisw; break;
|
||||
case 0b000000100010: op = rv_op_th_dcache_isw; break;
|
||||
case 0b000000101000: op = rv_op_th_dcache_cpal1; break;
|
||||
case 0b000000100100: op = rv_op_th_dcache_cval1; break;
|
||||
case 0b000000010000:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_icache_iall;
|
||||
}
|
||||
break;
|
||||
case 0b000000010001:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_icache_ialls;
|
||||
}
|
||||
break;
|
||||
case 0b000000111000: op = rv_op_th_icache_ipa; break;
|
||||
case 0b000000110000: op = rv_op_th_icache_iva; break;
|
||||
case 0b000000010101:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_l2cache_call;
|
||||
}
|
||||
break;
|
||||
case 0b000000010111:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_l2cache_ciall;
|
||||
}
|
||||
break;
|
||||
case 0b000000010110:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_l2cache_iall;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadcondmov(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 1:
|
||||
switch ((inst >> 25) & 0b1111111) {
|
||||
case 0b0100000: op = rv_op_th_mveqz; break;
|
||||
case 0b0100001: op = rv_op_th_mvnez; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadfmemidx(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 6:
|
||||
switch ((inst >> 27) & 0b11111) {
|
||||
case 8: op = rv_op_th_flrw; break;
|
||||
case 10: op = rv_op_th_flurw; break;
|
||||
case 12: op = rv_op_th_flrd; break;
|
||||
case 14: op = rv_op_th_flurd; break;
|
||||
}
|
||||
break;
|
||||
case 7:
|
||||
switch ((inst >> 27) & 0b11111) {
|
||||
case 8: op = rv_op_th_fsrw; break;
|
||||
case 10: op = rv_op_th_fsurw; break;
|
||||
case 12: op = rv_op_th_fsrd; break;
|
||||
case 14: op = rv_op_th_fsurd; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadfmv(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 1:
|
||||
switch ((inst >> 25) & 0b1111111) {
|
||||
case 0b1010000:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_fmv_hw_x;
|
||||
}
|
||||
break;
|
||||
case 0b1100000:
|
||||
if (((inst >> 20) & 0b11111) == 0) {
|
||||
op = rv_op_th_fmv_x_hw;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadmac(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 1:
|
||||
switch ((inst >> 25) & 0b1111111) {
|
||||
case 0b0010000: op = rv_op_th_mula; break;
|
||||
case 0b0010001: op = rv_op_th_muls; break;
|
||||
case 0b0010010: op = rv_op_th_mulaw; break;
|
||||
case 0b0010011: op = rv_op_th_mulsw; break;
|
||||
case 0b0010100: op = rv_op_th_mulah; break;
|
||||
case 0b0010101: op = rv_op_th_mulsh; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadmemidx(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 4:
|
||||
switch ((inst >> 27) & 0b11111) {
|
||||
case 0: op = rv_op_th_lrb; break;
|
||||
case 1: op = rv_op_th_lbib; break;
|
||||
case 2: op = rv_op_th_lurb; break;
|
||||
case 3: op = rv_op_th_lbia; break;
|
||||
case 4: op = rv_op_th_lrh; break;
|
||||
case 5: op = rv_op_th_lhib; break;
|
||||
case 6: op = rv_op_th_lurh; break;
|
||||
case 7: op = rv_op_th_lhia; break;
|
||||
case 8: op = rv_op_th_lrw; break;
|
||||
case 9: op = rv_op_th_lwib; break;
|
||||
case 10: op = rv_op_th_lurw; break;
|
||||
case 11: op = rv_op_th_lwia; break;
|
||||
case 12: op = rv_op_th_lrd; break;
|
||||
case 13: op = rv_op_th_ldib; break;
|
||||
case 14: op = rv_op_th_lurd; break;
|
||||
case 15: op = rv_op_th_ldia; break;
|
||||
case 16: op = rv_op_th_lrbu; break;
|
||||
case 17: op = rv_op_th_lbuib; break;
|
||||
case 18: op = rv_op_th_lurbu; break;
|
||||
case 19: op = rv_op_th_lbuia; break;
|
||||
case 20: op = rv_op_th_lrhu; break;
|
||||
case 21: op = rv_op_th_lhuib; break;
|
||||
case 22: op = rv_op_th_lurhu; break;
|
||||
case 23: op = rv_op_th_lhuia; break;
|
||||
case 24: op = rv_op_th_lrwu; break;
|
||||
case 25: op = rv_op_th_lwuib; break;
|
||||
case 26: op = rv_op_th_lurwu; break;
|
||||
case 27: op = rv_op_th_lwuia; break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
switch ((inst >> 27) & 0b11111) {
|
||||
case 0: op = rv_op_th_srb; break;
|
||||
case 1: op = rv_op_th_sbib; break;
|
||||
case 2: op = rv_op_th_surb; break;
|
||||
case 3: op = rv_op_th_sbia; break;
|
||||
case 4: op = rv_op_th_srh; break;
|
||||
case 5: op = rv_op_th_shib; break;
|
||||
case 6: op = rv_op_th_surh; break;
|
||||
case 7: op = rv_op_th_shia; break;
|
||||
case 8: op = rv_op_th_srw; break;
|
||||
case 9: op = rv_op_th_swib; break;
|
||||
case 10: op = rv_op_th_surw; break;
|
||||
case 11: op = rv_op_th_swia; break;
|
||||
case 12: op = rv_op_th_srd; break;
|
||||
case 13: op = rv_op_th_sdib; break;
|
||||
case 14: op = rv_op_th_surd; break;
|
||||
case 15: op = rv_op_th_sdia; break;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadmempair(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 4:
|
||||
switch ((inst >> 27) & 0b11111) {
|
||||
case 28: op = rv_op_th_lwd; break;
|
||||
case 30: op = rv_op_th_lwud; break;
|
||||
case 31: op = rv_op_th_ldd; break;
|
||||
}
|
||||
break;
|
||||
case 5:
|
||||
switch ((inst >> 27) & 0b11111) {
|
||||
case 28: op = rv_op_th_swd; break;
|
||||
case 31: op = rv_op_th_sdd; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
||||
|
||||
void decode_xtheadsync(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 2:
|
||||
/* custom-0 */
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 0:
|
||||
switch ((inst >> 25) & 0b1111111) {
|
||||
case 0b0000010: op = rv_op_th_sfence_vmas; break;
|
||||
case 0b0000000:
|
||||
switch ((inst >> 20) & 0b11111) {
|
||||
case 0b11000: op = rv_op_th_sync; break;
|
||||
case 0b11010: op = rv_op_th_sync_i; break;
|
||||
case 0b11011: op = rv_op_th_sync_is; break;
|
||||
case 0b11001: op = rv_op_th_sync_s; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
/* custom-0 */
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
28
disas/riscv-xthead.h
Normal file
28
disas/riscv-xthead.h
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* QEMU disassembler -- RISC-V specific header (xthead*).
|
||||
*
|
||||
* Copyright (c) 2023 VRULL GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DISAS_RISCV_XTHEAD_H
|
||||
#define DISAS_RISCV_XTHEAD_H
|
||||
|
||||
#include "disas/riscv.h"
|
||||
|
||||
extern const rv_opcode_data xthead_opcode_data[];
|
||||
|
||||
void decode_xtheadba(rv_decode *, rv_isa);
|
||||
void decode_xtheadbb(rv_decode *, rv_isa);
|
||||
void decode_xtheadbs(rv_decode *, rv_isa);
|
||||
void decode_xtheadcmo(rv_decode *, rv_isa);
|
||||
void decode_xtheadcondmov(rv_decode *, rv_isa);
|
||||
void decode_xtheadfmemidx(rv_decode *, rv_isa);
|
||||
void decode_xtheadfmv(rv_decode *, rv_isa);
|
||||
void decode_xtheadmac(rv_decode *, rv_isa);
|
||||
void decode_xtheadmemidx(rv_decode *, rv_isa);
|
||||
void decode_xtheadmempair(rv_decode *, rv_isa);
|
||||
void decode_xtheadsync(rv_decode *, rv_isa);
|
||||
|
||||
#endif /* DISAS_RISCV_XTHEAD_H */
|
41
disas/riscv-xventana.c
Normal file
41
disas/riscv-xventana.c
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* QEMU RISC-V Disassembler for xventana.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "disas/riscv.h"
|
||||
#include "disas/riscv-xventana.h"
|
||||
|
||||
typedef enum {
|
||||
/* 0 is reserved for rv_op_illegal. */
|
||||
ventana_op_vt_maskc = 1,
|
||||
ventana_op_vt_maskcn = 2,
|
||||
} rv_ventana_op;
|
||||
|
||||
const rv_opcode_data ventana_opcode_data[] = {
|
||||
{ "vt.illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "vt.maskc", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "vt.maskcn", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
void decode_xventanacondops(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
rv_inst inst = dec->inst;
|
||||
rv_opcode op = rv_op_illegal;
|
||||
|
||||
switch (((inst >> 0) & 0b11)) {
|
||||
case 3:
|
||||
switch (((inst >> 2) & 0b11111)) {
|
||||
case 30:
|
||||
switch (((inst >> 22) & 0b1111111000) | ((inst >> 12) & 0b0000000111)) {
|
||||
case 6: op = ventana_op_vt_maskc; break;
|
||||
case 7: op = ventana_op_vt_maskcn; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
dec->op = op;
|
||||
}
|
18
disas/riscv-xventana.h
Normal file
18
disas/riscv-xventana.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* QEMU disassembler -- RISC-V specific header (xventana*).
|
||||
*
|
||||
* Copyright (c) 2023 VRULL GmbH
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DISAS_RISCV_XVENTANA_H
|
||||
#define DISAS_RISCV_XVENTANA_H
|
||||
|
||||
#include "disas/riscv.h"
|
||||
|
||||
extern const rv_opcode_data ventana_opcode_data[];
|
||||
|
||||
void decode_xventanacondops(rv_decode*, rv_isa);
|
||||
|
||||
#endif /* DISAS_RISCV_XVENTANA_H */
|
559
disas/riscv.c
559
disas/riscv.c
@ -18,162 +18,17 @@
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "disas/dis-asm.h"
|
||||
#include "target/riscv/cpu_cfg.h"
|
||||
#include "disas/riscv.h"
|
||||
|
||||
/* types */
|
||||
|
||||
typedef uint64_t rv_inst;
|
||||
typedef uint16_t rv_opcode;
|
||||
|
||||
/* enums */
|
||||
/* Vendor extensions */
|
||||
#include "disas/riscv-xthead.h"
|
||||
#include "disas/riscv-xventana.h"
|
||||
|
||||
typedef enum {
|
||||
rv32,
|
||||
rv64,
|
||||
rv128
|
||||
} rv_isa;
|
||||
|
||||
typedef enum {
|
||||
rv_rm_rne = 0,
|
||||
rv_rm_rtz = 1,
|
||||
rv_rm_rdn = 2,
|
||||
rv_rm_rup = 3,
|
||||
rv_rm_rmm = 4,
|
||||
rv_rm_dyn = 7,
|
||||
} rv_rm;
|
||||
|
||||
typedef enum {
|
||||
rv_fence_i = 8,
|
||||
rv_fence_o = 4,
|
||||
rv_fence_r = 2,
|
||||
rv_fence_w = 1,
|
||||
} rv_fence;
|
||||
|
||||
typedef enum {
|
||||
rv_ireg_zero,
|
||||
rv_ireg_ra,
|
||||
rv_ireg_sp,
|
||||
rv_ireg_gp,
|
||||
rv_ireg_tp,
|
||||
rv_ireg_t0,
|
||||
rv_ireg_t1,
|
||||
rv_ireg_t2,
|
||||
rv_ireg_s0,
|
||||
rv_ireg_s1,
|
||||
rv_ireg_a0,
|
||||
rv_ireg_a1,
|
||||
rv_ireg_a2,
|
||||
rv_ireg_a3,
|
||||
rv_ireg_a4,
|
||||
rv_ireg_a5,
|
||||
rv_ireg_a6,
|
||||
rv_ireg_a7,
|
||||
rv_ireg_s2,
|
||||
rv_ireg_s3,
|
||||
rv_ireg_s4,
|
||||
rv_ireg_s5,
|
||||
rv_ireg_s6,
|
||||
rv_ireg_s7,
|
||||
rv_ireg_s8,
|
||||
rv_ireg_s9,
|
||||
rv_ireg_s10,
|
||||
rv_ireg_s11,
|
||||
rv_ireg_t3,
|
||||
rv_ireg_t4,
|
||||
rv_ireg_t5,
|
||||
rv_ireg_t6,
|
||||
} rv_ireg;
|
||||
|
||||
typedef enum {
|
||||
rvc_end,
|
||||
rvc_rd_eq_ra,
|
||||
rvc_rd_eq_x0,
|
||||
rvc_rs1_eq_x0,
|
||||
rvc_rs2_eq_x0,
|
||||
rvc_rs2_eq_rs1,
|
||||
rvc_rs1_eq_ra,
|
||||
rvc_imm_eq_zero,
|
||||
rvc_imm_eq_n1,
|
||||
rvc_imm_eq_p1,
|
||||
rvc_csr_eq_0x001,
|
||||
rvc_csr_eq_0x002,
|
||||
rvc_csr_eq_0x003,
|
||||
rvc_csr_eq_0xc00,
|
||||
rvc_csr_eq_0xc01,
|
||||
rvc_csr_eq_0xc02,
|
||||
rvc_csr_eq_0xc80,
|
||||
rvc_csr_eq_0xc81,
|
||||
rvc_csr_eq_0xc82,
|
||||
} rvc_constraint;
|
||||
|
||||
typedef enum {
|
||||
rv_codec_illegal,
|
||||
rv_codec_none,
|
||||
rv_codec_u,
|
||||
rv_codec_uj,
|
||||
rv_codec_i,
|
||||
rv_codec_i_sh5,
|
||||
rv_codec_i_sh6,
|
||||
rv_codec_i_sh7,
|
||||
rv_codec_i_csr,
|
||||
rv_codec_s,
|
||||
rv_codec_sb,
|
||||
rv_codec_r,
|
||||
rv_codec_r_m,
|
||||
rv_codec_r4_m,
|
||||
rv_codec_r_a,
|
||||
rv_codec_r_l,
|
||||
rv_codec_r_f,
|
||||
rv_codec_cb,
|
||||
rv_codec_cb_imm,
|
||||
rv_codec_cb_sh5,
|
||||
rv_codec_cb_sh6,
|
||||
rv_codec_ci,
|
||||
rv_codec_ci_sh5,
|
||||
rv_codec_ci_sh6,
|
||||
rv_codec_ci_16sp,
|
||||
rv_codec_ci_lwsp,
|
||||
rv_codec_ci_ldsp,
|
||||
rv_codec_ci_lqsp,
|
||||
rv_codec_ci_li,
|
||||
rv_codec_ci_lui,
|
||||
rv_codec_ci_none,
|
||||
rv_codec_ciw_4spn,
|
||||
rv_codec_cj,
|
||||
rv_codec_cj_jal,
|
||||
rv_codec_cl_lw,
|
||||
rv_codec_cl_ld,
|
||||
rv_codec_cl_lq,
|
||||
rv_codec_cr,
|
||||
rv_codec_cr_mv,
|
||||
rv_codec_cr_jalr,
|
||||
rv_codec_cr_jr,
|
||||
rv_codec_cs,
|
||||
rv_codec_cs_sw,
|
||||
rv_codec_cs_sd,
|
||||
rv_codec_cs_sq,
|
||||
rv_codec_css_swsp,
|
||||
rv_codec_css_sdsp,
|
||||
rv_codec_css_sqsp,
|
||||
rv_codec_k_bs,
|
||||
rv_codec_k_rnum,
|
||||
rv_codec_v_r,
|
||||
rv_codec_v_ldst,
|
||||
rv_codec_v_i,
|
||||
rv_codec_vsetvli,
|
||||
rv_codec_vsetivli,
|
||||
rv_codec_zcb_ext,
|
||||
rv_codec_zcb_mul,
|
||||
rv_codec_zcb_lb,
|
||||
rv_codec_zcb_lh,
|
||||
rv_codec_zcmp_cm_pushpop,
|
||||
rv_codec_zcmp_cm_mv,
|
||||
rv_codec_zcmt_jt,
|
||||
} rv_codec;
|
||||
|
||||
typedef enum {
|
||||
rv_op_illegal = 0,
|
||||
/* 0 is reserved for rv_op_illegal. */
|
||||
rv_op_lui = 1,
|
||||
rv_op_auipc = 2,
|
||||
rv_op_jal = 3,
|
||||
@ -964,53 +819,51 @@ typedef enum {
|
||||
rv_op_cm_jalt = 788,
|
||||
rv_op_czero_eqz = 789,
|
||||
rv_op_czero_nez = 790,
|
||||
rv_op_fcvt_bf16_s = 791,
|
||||
rv_op_fcvt_s_bf16 = 792,
|
||||
rv_op_vfncvtbf16_f_f_w = 793,
|
||||
rv_op_vfwcvtbf16_f_f_v = 794,
|
||||
rv_op_vfwmaccbf16_vv = 795,
|
||||
rv_op_vfwmaccbf16_vf = 796,
|
||||
rv_op_flh = 797,
|
||||
rv_op_fsh = 798,
|
||||
rv_op_fmv_h_x = 799,
|
||||
rv_op_fmv_x_h = 800,
|
||||
rv_op_fli_s = 801,
|
||||
rv_op_fli_d = 802,
|
||||
rv_op_fli_q = 803,
|
||||
rv_op_fli_h = 804,
|
||||
rv_op_fminm_s = 805,
|
||||
rv_op_fmaxm_s = 806,
|
||||
rv_op_fminm_d = 807,
|
||||
rv_op_fmaxm_d = 808,
|
||||
rv_op_fminm_q = 809,
|
||||
rv_op_fmaxm_q = 810,
|
||||
rv_op_fminm_h = 811,
|
||||
rv_op_fmaxm_h = 812,
|
||||
rv_op_fround_s = 813,
|
||||
rv_op_froundnx_s = 814,
|
||||
rv_op_fround_d = 815,
|
||||
rv_op_froundnx_d = 816,
|
||||
rv_op_fround_q = 817,
|
||||
rv_op_froundnx_q = 818,
|
||||
rv_op_fround_h = 819,
|
||||
rv_op_froundnx_h = 820,
|
||||
rv_op_fcvtmod_w_d = 821,
|
||||
rv_op_fmvh_x_d = 822,
|
||||
rv_op_fmvp_d_x = 823,
|
||||
rv_op_fmvh_x_q = 824,
|
||||
rv_op_fmvp_q_x = 825,
|
||||
rv_op_fleq_s = 826,
|
||||
rv_op_fltq_s = 827,
|
||||
rv_op_fleq_d = 828,
|
||||
rv_op_fltq_d = 829,
|
||||
rv_op_fleq_q = 830,
|
||||
rv_op_fltq_q = 831,
|
||||
rv_op_fleq_h = 832,
|
||||
rv_op_fltq_h = 833,
|
||||
} rv_op;
|
||||
|
||||
/* structures */
|
||||
|
||||
typedef struct {
|
||||
RISCVCPUConfig *cfg;
|
||||
uint64_t pc;
|
||||
uint64_t inst;
|
||||
int32_t imm;
|
||||
uint16_t op;
|
||||
uint8_t codec;
|
||||
uint8_t rd;
|
||||
uint8_t rs1;
|
||||
uint8_t rs2;
|
||||
uint8_t rs3;
|
||||
uint8_t rm;
|
||||
uint8_t pred;
|
||||
uint8_t succ;
|
||||
uint8_t aq;
|
||||
uint8_t rl;
|
||||
uint8_t bs;
|
||||
uint8_t rnum;
|
||||
uint8_t vm;
|
||||
uint32_t vzimm;
|
||||
uint8_t rlist;
|
||||
} rv_decode;
|
||||
|
||||
typedef struct {
|
||||
const int op;
|
||||
const rvc_constraint *constraints;
|
||||
} rv_comp_data;
|
||||
|
||||
enum {
|
||||
rvcd_imm_nz = 0x1
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
const char * const name;
|
||||
const rv_codec codec;
|
||||
const char * const format;
|
||||
const rv_comp_data *pseudo;
|
||||
const short decomp_rv32;
|
||||
const short decomp_rv64;
|
||||
const short decomp_rv128;
|
||||
const short decomp_data;
|
||||
} rv_opcode_data;
|
||||
|
||||
/* register names */
|
||||
|
||||
static const char rv_ireg_name_sym[32][5] = {
|
||||
@ -1034,78 +887,22 @@ static const char rv_vreg_name_sym[32][4] = {
|
||||
"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
|
||||
};
|
||||
|
||||
/* instruction formats */
|
||||
|
||||
#define rv_fmt_none "O\t"
|
||||
#define rv_fmt_rs1 "O\t1"
|
||||
#define rv_fmt_offset "O\to"
|
||||
#define rv_fmt_pred_succ "O\tp,s"
|
||||
#define rv_fmt_rs1_rs2 "O\t1,2"
|
||||
#define rv_fmt_rd_imm "O\t0,i"
|
||||
#define rv_fmt_rd_offset "O\t0,o"
|
||||
#define rv_fmt_rd_rs1_rs2 "O\t0,1,2"
|
||||
#define rv_fmt_frd_rs1 "O\t3,1"
|
||||
#define rv_fmt_frd_frs1 "O\t3,4"
|
||||
#define rv_fmt_rd_frs1 "O\t0,4"
|
||||
#define rv_fmt_rd_frs1_frs2 "O\t0,4,5"
|
||||
#define rv_fmt_frd_frs1_frs2 "O\t3,4,5"
|
||||
#define rv_fmt_rm_frd_frs1 "O\tr,3,4"
|
||||
#define rv_fmt_rm_frd_rs1 "O\tr,3,1"
|
||||
#define rv_fmt_rm_rd_frs1 "O\tr,0,4"
|
||||
#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5"
|
||||
#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6"
|
||||
#define rv_fmt_rd_rs1_imm "O\t0,1,i"
|
||||
#define rv_fmt_rd_rs1_offset "O\t0,1,i"
|
||||
#define rv_fmt_rd_offset_rs1 "O\t0,i(1)"
|
||||
#define rv_fmt_frd_offset_rs1 "O\t3,i(1)"
|
||||
#define rv_fmt_rd_csr_rs1 "O\t0,c,1"
|
||||
#define rv_fmt_rd_csr_zimm "O\t0,c,7"
|
||||
#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)"
|
||||
#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)"
|
||||
#define rv_fmt_rs1_rs2_offset "O\t1,2,o"
|
||||
#define rv_fmt_rs2_rs1_offset "O\t2,1,o"
|
||||
#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)"
|
||||
#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)"
|
||||
#define rv_fmt_rd "O\t0"
|
||||
#define rv_fmt_rd_zimm "O\t0,7"
|
||||
#define rv_fmt_rd_rs1 "O\t0,1"
|
||||
#define rv_fmt_rd_rs2 "O\t0,2"
|
||||
#define rv_fmt_rs1_offset "O\t1,o"
|
||||
#define rv_fmt_rs2_offset "O\t2,o"
|
||||
#define rv_fmt_rs1_rs2_bs "O\t1,2,b"
|
||||
#define rv_fmt_rd_rs1_rnum "O\t0,1,n"
|
||||
#define rv_fmt_ldst_vd_rs1_vm "O\tD,(1)m"
|
||||
#define rv_fmt_ldst_vd_rs1_rs2_vm "O\tD,(1),2m"
|
||||
#define rv_fmt_ldst_vd_rs1_vs2_vm "O\tD,(1),Fm"
|
||||
#define rv_fmt_vd_vs2_vs1 "O\tD,F,E"
|
||||
#define rv_fmt_vd_vs2_vs1_vl "O\tD,F,El"
|
||||
#define rv_fmt_vd_vs2_vs1_vm "O\tD,F,Em"
|
||||
#define rv_fmt_vd_vs2_rs1_vl "O\tD,F,1l"
|
||||
#define rv_fmt_vd_vs2_fs1_vl "O\tD,F,4l"
|
||||
#define rv_fmt_vd_vs2_rs1_vm "O\tD,F,1m"
|
||||
#define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m"
|
||||
#define rv_fmt_vd_vs2_imm_vl "O\tD,F,il"
|
||||
#define rv_fmt_vd_vs2_imm_vm "O\tD,F,im"
|
||||
#define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um"
|
||||
#define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm"
|
||||
#define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm"
|
||||
#define rv_fmt_vd_fs1_vs2_vm "O\tD,4,Fm"
|
||||
#define rv_fmt_vd_vs1 "O\tD,E"
|
||||
#define rv_fmt_vd_rs1 "O\tD,1"
|
||||
#define rv_fmt_vd_fs1 "O\tD,4"
|
||||
#define rv_fmt_vd_imm "O\tD,i"
|
||||
#define rv_fmt_vd_vs2 "O\tD,F"
|
||||
#define rv_fmt_vd_vs2_vm "O\tD,Fm"
|
||||
#define rv_fmt_rd_vs2_vm "O\t0,Fm"
|
||||
#define rv_fmt_rd_vs2 "O\t0,F"
|
||||
#define rv_fmt_fd_vs2 "O\t3,F"
|
||||
#define rv_fmt_vd_vm "O\tDm"
|
||||
#define rv_fmt_vsetvli "O\t0,1,v"
|
||||
#define rv_fmt_vsetivli "O\t0,u,v"
|
||||
#define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)"
|
||||
#define rv_fmt_push_rlist "O\tx,-i"
|
||||
#define rv_fmt_pop_rlist "O\tx,i"
|
||||
#define rv_fmt_zcmt_index "O\ti"
|
||||
/* The FLI.[HSDQ] numeric constants (0.0 for symbolic constants).
|
||||
* The constants use the hex floating-point literal representation
|
||||
* that is printed when using the printf %a format specifier,
|
||||
* which matches the output that is generated by the disassembler.
|
||||
*/
|
||||
static const char rv_fli_name_const[32][9] =
|
||||
{
|
||||
"0x1p+0", "min", "0x1p-16", "0x1p-15",
|
||||
"0x1p-8", "0x1p-7", "0x1p-4", "0x1p-3",
|
||||
"0x1p-2", "0x1.4p-2", "0x1.8p-2", "0x1.cp-2",
|
||||
"0x1p-1", "0x1.4p-1", "0x1.8p-1", "0x1.cp-1",
|
||||
"0x1p+0", "0x1.4p+0", "0x1.8p+0", "0x1.cp+0",
|
||||
"0x1p+1", "0x1.4p+1", "0x1.8p+1", "0x1p+2",
|
||||
"0x1p+3", "0x1p+4", "0x1p+7", "0x1p+8",
|
||||
"0x1p+15", "0x1p+16", "inf", "nan"
|
||||
};
|
||||
|
||||
/* pseudo-instruction constraints */
|
||||
|
||||
@ -1336,7 +1133,7 @@ static const rv_comp_data rvcp_fsgnjx_q[] = {
|
||||
|
||||
/* instruction metadata */
|
||||
|
||||
const rv_opcode_data opcode_data[] = {
|
||||
const rv_opcode_data rvi_opcode_data[] = {
|
||||
{ "illegal", rv_codec_illegal, rv_fmt_none, NULL, 0, 0, 0 },
|
||||
{ "lui", rv_codec_u, rv_fmt_rd_imm, NULL, 0, 0, 0 },
|
||||
{ "auipc", rv_codec_u, rv_fmt_rd_offset, NULL, 0, 0, 0 },
|
||||
@ -2168,6 +1965,49 @@ const rv_opcode_data opcode_data[] = {
|
||||
{ "cm.jalt", rv_codec_zcmt_jt, rv_fmt_zcmt_index, NULL, 0 },
|
||||
{ "czero.eqz", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "czero.nez", rv_codec_r, rv_fmt_rd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "fcvt.bf16.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fcvt.s.bf16", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "vfncvtbf16.f.f.w", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 },
|
||||
{ "vfwcvtbf16.f.f.v", rv_codec_v_r, rv_fmt_vd_vs2_vm, NULL, 0, 0, 0 },
|
||||
{ "vfwmaccbf16.vv", rv_codec_v_r, rv_fmt_vd_vs1_vs2_vm, NULL, 0, 0, 0 },
|
||||
{ "vfwmaccbf16.vf", rv_codec_v_r, rv_fmt_vd_fs1_vs2_vm, NULL, 0, 0, 0 },
|
||||
{ "flh", rv_codec_i, rv_fmt_frd_offset_rs1, NULL, 0, 0, 0 },
|
||||
{ "fsh", rv_codec_s, rv_fmt_frs2_offset_rs1, NULL, 0, 0, 0 },
|
||||
{ "fmv.h.x", rv_codec_r, rv_fmt_frd_rs1, NULL, 0, 0, 0 },
|
||||
{ "fmv.x.h", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fli.s", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
|
||||
{ "fli.d", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
|
||||
{ "fli.q", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
|
||||
{ "fli.h", rv_codec_fli, rv_fmt_fli, NULL, 0, 0, 0 },
|
||||
{ "fminm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fmaxm.s", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fminm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fmaxm.d", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fminm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fmaxm.q", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fminm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fmaxm.h", rv_codec_r, rv_fmt_frd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fround.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "froundnx.s", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fround.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "froundnx.d", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fround.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "froundnx.q", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fround.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "froundnx.h", rv_codec_r_m, rv_fmt_rm_frd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fcvtmod.w.d", rv_codec_r_m, rv_fmt_rm_rd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fmvh.x.d", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fmvp.d.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "fmvh.x.q", rv_codec_r, rv_fmt_rd_frs1, NULL, 0, 0, 0 },
|
||||
{ "fmvp.q.x", rv_codec_r, rv_fmt_frd_rs1_rs2, NULL, 0, 0, 0 },
|
||||
{ "fleq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fltq.s", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fleq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fltq.d", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fleq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fltq.q", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fleq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
{ "fltq.h", rv_codec_r, rv_fmt_rd_frs1_frs2, NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
/* CSR names */
|
||||
@ -2643,6 +2483,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 3: op = rv_op_vloxei8_v; break;
|
||||
}
|
||||
break;
|
||||
case 1: op = rv_op_flh; break;
|
||||
case 2: op = rv_op_flw; break;
|
||||
case 3: op = rv_op_fld; break;
|
||||
case 4: op = rv_op_flq; break;
|
||||
@ -2846,6 +2687,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 3: op = rv_op_vsoxei8_v; break;
|
||||
}
|
||||
break;
|
||||
case 1: op = rv_op_fsh; break;
|
||||
case 2: op = rv_op_fsw; break;
|
||||
case 3: op = rv_op_fsd; break;
|
||||
case 4: op = rv_op_fsq; break;
|
||||
@ -3105,36 +2947,62 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 0: op = rv_op_fmin_s; break;
|
||||
case 1: op = rv_op_fmax_s; break;
|
||||
case 2: op = rv_op_fminm_s; break;
|
||||
case 3: op = rv_op_fmaxm_s; break;
|
||||
}
|
||||
break;
|
||||
case 21:
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 0: op = rv_op_fmin_d; break;
|
||||
case 1: op = rv_op_fmax_d; break;
|
||||
case 2: op = rv_op_fminm_d; break;
|
||||
case 3: op = rv_op_fmaxm_d; break;
|
||||
}
|
||||
break;
|
||||
case 22:
|
||||
switch (((inst >> 12) & 0b111)) {
|
||||
case 2: op = rv_op_fminm_h; break;
|
||||
case 3: op = rv_op_fmaxm_h; break;
|
||||
}
|
||||
break;
|
||||
case 23:
|
||||
switch ((inst >> 12) & 0b111) {
|
||||
case 0: op = rv_op_fmin_q; break;
|
||||
case 1: op = rv_op_fmax_q; break;
|
||||
case 2: op = rv_op_fminm_q; break;
|
||||
case 3: op = rv_op_fmaxm_q; break;
|
||||
}
|
||||
break;
|
||||
case 32:
|
||||
switch ((inst >> 20) & 0b11111) {
|
||||
case 1: op = rv_op_fcvt_s_d; break;
|
||||
case 3: op = rv_op_fcvt_s_q; break;
|
||||
case 4: op = rv_op_fround_s; break;
|
||||
case 5: op = rv_op_froundnx_s; break;
|
||||
case 6: op = rv_op_fcvt_s_bf16; break;
|
||||
}
|
||||
break;
|
||||
case 33:
|
||||
switch ((inst >> 20) & 0b11111) {
|
||||
case 0: op = rv_op_fcvt_d_s; break;
|
||||
case 3: op = rv_op_fcvt_d_q; break;
|
||||
case 4: op = rv_op_fround_d; break;
|
||||
case 5: op = rv_op_froundnx_d; break;
|
||||
}
|
||||
break;
|
||||
case 34:
|
||||
switch (((inst >> 20) & 0b11111)) {
|
||||
case 4: op = rv_op_fround_h; break;
|
||||
case 5: op = rv_op_froundnx_h; break;
|
||||
case 8: op = rv_op_fcvt_bf16_s; break;
|
||||
}
|
||||
break;
|
||||
case 35:
|
||||
switch ((inst >> 20) & 0b11111) {
|
||||
case 0: op = rv_op_fcvt_q_s; break;
|
||||
case 1: op = rv_op_fcvt_q_d; break;
|
||||
case 4: op = rv_op_fround_q; break;
|
||||
case 5: op = rv_op_froundnx_q; break;
|
||||
}
|
||||
break;
|
||||
case 44:
|
||||
@ -3157,6 +3025,8 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 0: op = rv_op_fle_s; break;
|
||||
case 1: op = rv_op_flt_s; break;
|
||||
case 2: op = rv_op_feq_s; break;
|
||||
case 4: op = rv_op_fleq_s; break;
|
||||
case 5: op = rv_op_fltq_s; break;
|
||||
}
|
||||
break;
|
||||
case 81:
|
||||
@ -3164,6 +3034,14 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 0: op = rv_op_fle_d; break;
|
||||
case 1: op = rv_op_flt_d; break;
|
||||
case 2: op = rv_op_feq_d; break;
|
||||
case 4: op = rv_op_fleq_d; break;
|
||||
case 5: op = rv_op_fltq_d; break;
|
||||
}
|
||||
break;
|
||||
case 82:
|
||||
switch (((inst >> 12) & 0b111)) {
|
||||
case 4: op = rv_op_fleq_h; break;
|
||||
case 5: op = rv_op_fltq_h; break;
|
||||
}
|
||||
break;
|
||||
case 83:
|
||||
@ -3171,6 +3049,18 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 0: op = rv_op_fle_q; break;
|
||||
case 1: op = rv_op_flt_q; break;
|
||||
case 2: op = rv_op_feq_q; break;
|
||||
case 4: op = rv_op_fleq_q; break;
|
||||
case 5: op = rv_op_fltq_q; break;
|
||||
}
|
||||
break;
|
||||
case 89:
|
||||
switch (((inst >> 12) & 0b111)) {
|
||||
case 0: op = rv_op_fmvp_d_x; break;
|
||||
}
|
||||
break;
|
||||
case 91:
|
||||
switch (((inst >> 12) & 0b111)) {
|
||||
case 0: op = rv_op_fmvp_q_x; break;
|
||||
}
|
||||
break;
|
||||
case 96:
|
||||
@ -3187,6 +3077,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 1: op = rv_op_fcvt_wu_d; break;
|
||||
case 2: op = rv_op_fcvt_l_d; break;
|
||||
case 3: op = rv_op_fcvt_lu_d; break;
|
||||
case 8: op = rv_op_fcvtmod_w_d; break;
|
||||
}
|
||||
break;
|
||||
case 99:
|
||||
@ -3233,6 +3124,13 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
((inst >> 12) & 0b00000111)) {
|
||||
case 0: op = rv_op_fmv_x_d; break;
|
||||
case 1: op = rv_op_fclass_d; break;
|
||||
case 8: op = rv_op_fmvh_x_d; break;
|
||||
}
|
||||
break;
|
||||
case 114:
|
||||
switch (((inst >> 17) & 0b11111000) |
|
||||
((inst >> 12) & 0b00000111)) {
|
||||
case 0: op = rv_op_fmv_x_h; break;
|
||||
}
|
||||
break;
|
||||
case 115:
|
||||
@ -3240,24 +3138,35 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
((inst >> 12) & 0b00000111)) {
|
||||
case 0: op = rv_op_fmv_x_q; break;
|
||||
case 1: op = rv_op_fclass_q; break;
|
||||
case 8: op = rv_op_fmvh_x_q; break;
|
||||
}
|
||||
break;
|
||||
case 120:
|
||||
switch (((inst >> 17) & 0b11111000) |
|
||||
((inst >> 12) & 0b00000111)) {
|
||||
case 0: op = rv_op_fmv_s_x; break;
|
||||
case 8: op = rv_op_fli_s; break;
|
||||
}
|
||||
break;
|
||||
case 121:
|
||||
switch (((inst >> 17) & 0b11111000) |
|
||||
((inst >> 12) & 0b00000111)) {
|
||||
case 0: op = rv_op_fmv_d_x; break;
|
||||
case 8: op = rv_op_fli_d; break;
|
||||
}
|
||||
break;
|
||||
case 122:
|
||||
switch (((inst >> 17) & 0b11111000) |
|
||||
((inst >> 12) & 0b00000111)) {
|
||||
case 0: op = rv_op_fmv_h_x; break;
|
||||
case 8: op = rv_op_fli_h; break;
|
||||
}
|
||||
break;
|
||||
case 123:
|
||||
switch (((inst >> 17) & 0b11111000) |
|
||||
((inst >> 12) & 0b00000111)) {
|
||||
case 0: op = rv_op_fmv_q_x; break;
|
||||
case 8: op = rv_op_fli_q; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -3350,6 +3259,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 10: op = rv_op_vfwcvt_f_xu_v; break;
|
||||
case 11: op = rv_op_vfwcvt_f_x_v; break;
|
||||
case 12: op = rv_op_vfwcvt_f_f_v; break;
|
||||
case 13: op = rv_op_vfwcvtbf16_f_f_v; break;
|
||||
case 14: op = rv_op_vfwcvt_rtz_xu_f_v; break;
|
||||
case 15: op = rv_op_vfwcvt_rtz_x_f_v; break;
|
||||
case 16: op = rv_op_vfncvt_xu_f_w; break;
|
||||
@ -3360,6 +3270,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 21: op = rv_op_vfncvt_rod_f_f_w; break;
|
||||
case 22: op = rv_op_vfncvt_rtz_xu_f_w; break;
|
||||
case 23: op = rv_op_vfncvt_rtz_x_f_w; break;
|
||||
case 29: op = rv_op_vfncvtbf16_f_f_w; break;
|
||||
}
|
||||
break;
|
||||
case 19:
|
||||
@ -3391,6 +3302,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 52: op = rv_op_vfwadd_wv; break;
|
||||
case 54: op = rv_op_vfwsub_wv; break;
|
||||
case 56: op = rv_op_vfwmul_vv; break;
|
||||
case 59: op = rv_op_vfwmaccbf16_vv; break;
|
||||
case 60: op = rv_op_vfwmacc_vv; break;
|
||||
case 61: op = rv_op_vfwnmacc_vv; break;
|
||||
case 62: op = rv_op_vfwmsac_vv; break;
|
||||
@ -3629,6 +3541,7 @@ static void decode_inst_opcode(rv_decode *dec, rv_isa isa)
|
||||
case 52: op = rv_op_vfwadd_wf; break;
|
||||
case 54: op = rv_op_vfwsub_wf; break;
|
||||
case 56: op = rv_op_vfwmul_vf; break;
|
||||
case 59: op = rv_op_vfwmaccbf16_vf; break;
|
||||
case 60: op = rv_op_vfwmacc_vf; break;
|
||||
case 61: op = rv_op_vfwnmacc_vf; break;
|
||||
case 62: op = rv_op_vfwmsac_vf; break;
|
||||
@ -4134,6 +4047,26 @@ static uint32_t operand_zcmp_rlist(rv_inst inst)
|
||||
return ((inst << 56) >> 60);
|
||||
}
|
||||
|
||||
static uint32_t operand_imm6(rv_inst inst)
|
||||
{
|
||||
return (inst << 38) >> 60;
|
||||
}
|
||||
|
||||
static uint32_t operand_imm2(rv_inst inst)
|
||||
{
|
||||
return (inst << 37) >> 62;
|
||||
}
|
||||
|
||||
static uint32_t operand_immh(rv_inst inst)
|
||||
{
|
||||
return (inst << 32) >> 58;
|
||||
}
|
||||
|
||||
static uint32_t operand_imml(rv_inst inst)
|
||||
{
|
||||
return (inst << 38) >> 58;
|
||||
}
|
||||
|
||||
static uint32_t calculate_stack_adj(rv_isa isa, uint32_t rlist, uint32_t spimm)
|
||||
{
|
||||
int xlen_bytes_log2 = isa == rv64 ? 3 : 2;
|
||||
@ -4157,6 +4090,7 @@ static uint32_t operand_tbl_index(rv_inst inst)
|
||||
|
||||
static void decode_inst_operands(rv_decode *dec, rv_isa isa)
|
||||
{
|
||||
const rv_opcode_data *opcode_data = dec->opcode_data;
|
||||
rv_inst inst = dec->inst;
|
||||
dec->codec = opcode_data[dec->op].codec;
|
||||
switch (dec->codec) {
|
||||
@ -4496,6 +4430,42 @@ static void decode_inst_operands(rv_decode *dec, rv_isa isa)
|
||||
break;
|
||||
case rv_codec_zcmt_jt:
|
||||
dec->imm = operand_tbl_index(inst);
|
||||
break;
|
||||
case rv_codec_fli:
|
||||
dec->rd = operand_rd(inst);
|
||||
dec->imm = operand_rs1(inst);
|
||||
break;
|
||||
case rv_codec_r2_imm5:
|
||||
dec->rd = operand_rd(inst);
|
||||
dec->rs1 = operand_rs1(inst);
|
||||
dec->imm = operand_rs2(inst);
|
||||
break;
|
||||
case rv_codec_r2:
|
||||
dec->rd = operand_rd(inst);
|
||||
dec->rs1 = operand_rs1(inst);
|
||||
break;
|
||||
case rv_codec_r2_imm6:
|
||||
dec->rd = operand_rd(inst);
|
||||
dec->rs1 = operand_rs1(inst);
|
||||
dec->imm = operand_imm6(inst);
|
||||
break;
|
||||
case rv_codec_r_imm2:
|
||||
dec->rd = operand_rd(inst);
|
||||
dec->rs1 = operand_rs1(inst);
|
||||
dec->rs2 = operand_rs2(inst);
|
||||
dec->imm = operand_imm2(inst);
|
||||
break;
|
||||
case rv_codec_r2_immhl:
|
||||
dec->rd = operand_rd(inst);
|
||||
dec->rs1 = operand_rs1(inst);
|
||||
dec->imm = operand_immh(inst);
|
||||
dec->imm1 = operand_imml(inst);
|
||||
break;
|
||||
case rv_codec_r2_imm2_imm5:
|
||||
dec->rd = operand_rd(inst);
|
||||
dec->rs1 = operand_rs1(inst);
|
||||
dec->imm = sextract32(operand_rs2(inst), 0, 5);
|
||||
dec->imm1 = operand_imm2(inst);
|
||||
break;
|
||||
};
|
||||
}
|
||||
@ -4639,6 +4609,7 @@ static void append(char *s1, const char *s2, size_t n)
|
||||
|
||||
static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
|
||||
{
|
||||
const rv_opcode_data *opcode_data = dec->opcode_data;
|
||||
char tmp[64];
|
||||
const char *fmt;
|
||||
|
||||
@ -4709,6 +4680,10 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
|
||||
snprintf(tmp, sizeof(tmp), "%u", ((uint32_t)dec->imm & 0b11111));
|
||||
append(buf, tmp, buflen);
|
||||
break;
|
||||
case 'j':
|
||||
snprintf(tmp, sizeof(tmp), "%d", dec->imm1);
|
||||
append(buf, tmp, buflen);
|
||||
break;
|
||||
case 'o':
|
||||
snprintf(tmp, sizeof(tmp), "%d", dec->imm);
|
||||
append(buf, tmp, buflen);
|
||||
@ -4869,6 +4844,9 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
|
||||
append(buf, tmp, buflen);
|
||||
break;
|
||||
}
|
||||
case 'h':
|
||||
append(buf, rv_fli_name_const[dec->imm], buflen);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -4880,6 +4858,7 @@ static void format_inst(char *buf, size_t buflen, size_t tab, rv_decode *dec)
|
||||
|
||||
static void decode_inst_lift_pseudo(rv_decode *dec)
|
||||
{
|
||||
const rv_opcode_data *opcode_data = dec->opcode_data;
|
||||
const rv_comp_data *comp_data = opcode_data[dec->op].pseudo;
|
||||
if (!comp_data) {
|
||||
return;
|
||||
@ -4898,6 +4877,7 @@ static void decode_inst_lift_pseudo(rv_decode *dec)
|
||||
|
||||
static void decode_inst_decompress_rv32(rv_decode *dec)
|
||||
{
|
||||
const rv_opcode_data *opcode_data = dec->opcode_data;
|
||||
int decomp_op = opcode_data[dec->op].decomp_rv32;
|
||||
if (decomp_op != rv_op_illegal) {
|
||||
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
|
||||
@ -4912,6 +4892,7 @@ static void decode_inst_decompress_rv32(rv_decode *dec)
|
||||
|
||||
static void decode_inst_decompress_rv64(rv_decode *dec)
|
||||
{
|
||||
const rv_opcode_data *opcode_data = dec->opcode_data;
|
||||
int decomp_op = opcode_data[dec->op].decomp_rv64;
|
||||
if (decomp_op != rv_op_illegal) {
|
||||
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
|
||||
@ -4926,6 +4907,7 @@ static void decode_inst_decompress_rv64(rv_decode *dec)
|
||||
|
||||
static void decode_inst_decompress_rv128(rv_decode *dec)
|
||||
{
|
||||
const rv_opcode_data *opcode_data = dec->opcode_data;
|
||||
int decomp_op = opcode_data[dec->op].decomp_rv128;
|
||||
if (decomp_op != rv_op_illegal) {
|
||||
if ((opcode_data[dec->op].decomp_data & rvcd_imm_nz)
|
||||
@ -4963,7 +4945,44 @@ disasm_inst(char *buf, size_t buflen, rv_isa isa, uint64_t pc, rv_inst inst,
|
||||
dec.pc = pc;
|
||||
dec.inst = inst;
|
||||
dec.cfg = cfg;
|
||||
decode_inst_opcode(&dec, isa);
|
||||
|
||||
static const struct {
|
||||
bool (*guard_func)(const RISCVCPUConfig *);
|
||||
const rv_opcode_data *opcode_data;
|
||||
void (*decode_func)(rv_decode *, rv_isa);
|
||||
} decoders[] = {
|
||||
{ always_true_p, rvi_opcode_data, decode_inst_opcode },
|
||||
{ has_xtheadba_p, xthead_opcode_data, decode_xtheadba },
|
||||
{ has_xtheadbb_p, xthead_opcode_data, decode_xtheadbb },
|
||||
{ has_xtheadbs_p, xthead_opcode_data, decode_xtheadbs },
|
||||
{ has_xtheadcmo_p, xthead_opcode_data, decode_xtheadcmo },
|
||||
{ has_xtheadcondmov_p, xthead_opcode_data, decode_xtheadcondmov },
|
||||
{ has_xtheadfmemidx_p, xthead_opcode_data, decode_xtheadfmemidx },
|
||||
{ has_xtheadfmv_p, xthead_opcode_data, decode_xtheadfmv },
|
||||
{ has_xtheadmac_p, xthead_opcode_data, decode_xtheadmac },
|
||||
{ has_xtheadmemidx_p, xthead_opcode_data, decode_xtheadmemidx },
|
||||
{ has_xtheadmempair_p, xthead_opcode_data, decode_xtheadmempair },
|
||||
{ has_xtheadsync_p, xthead_opcode_data, decode_xtheadsync },
|
||||
{ has_XVentanaCondOps_p, ventana_opcode_data, decode_xventanacondops },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(decoders); i++) {
|
||||
bool (*guard_func)(const RISCVCPUConfig *) = decoders[i].guard_func;
|
||||
const rv_opcode_data *opcode_data = decoders[i].opcode_data;
|
||||
void (*decode_func)(rv_decode *, rv_isa) = decoders[i].decode_func;
|
||||
|
||||
if (guard_func(cfg)) {
|
||||
dec.opcode_data = opcode_data;
|
||||
decode_func(&dec, isa);
|
||||
if (dec.op != rv_op_illegal)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dec.op == rv_op_illegal) {
|
||||
dec.opcode_data = rvi_opcode_data;
|
||||
}
|
||||
|
||||
decode_inst_operands(&dec, isa);
|
||||
decode_inst_decompress(&dec, isa);
|
||||
decode_inst_lift_pseudo(&dec);
|
||||
|
302
disas/riscv.h
Normal file
302
disas/riscv.h
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* QEMU disassembler -- RISC-V specific header.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DISAS_RISCV_H
|
||||
#define DISAS_RISCV_H
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "target/riscv/cpu_cfg.h"
|
||||
|
||||
/* types */
|
||||
|
||||
typedef uint64_t rv_inst;
|
||||
typedef uint16_t rv_opcode;
|
||||
|
||||
/* enums */
|
||||
|
||||
typedef enum {
|
||||
rv32,
|
||||
rv64,
|
||||
rv128
|
||||
} rv_isa;
|
||||
|
||||
typedef enum {
|
||||
rv_rm_rne = 0,
|
||||
rv_rm_rtz = 1,
|
||||
rv_rm_rdn = 2,
|
||||
rv_rm_rup = 3,
|
||||
rv_rm_rmm = 4,
|
||||
rv_rm_dyn = 7,
|
||||
} rv_rm;
|
||||
|
||||
typedef enum {
|
||||
rv_fence_i = 8,
|
||||
rv_fence_o = 4,
|
||||
rv_fence_r = 2,
|
||||
rv_fence_w = 1,
|
||||
} rv_fence;
|
||||
|
||||
typedef enum {
|
||||
rv_ireg_zero,
|
||||
rv_ireg_ra,
|
||||
rv_ireg_sp,
|
||||
rv_ireg_gp,
|
||||
rv_ireg_tp,
|
||||
rv_ireg_t0,
|
||||
rv_ireg_t1,
|
||||
rv_ireg_t2,
|
||||
rv_ireg_s0,
|
||||
rv_ireg_s1,
|
||||
rv_ireg_a0,
|
||||
rv_ireg_a1,
|
||||
rv_ireg_a2,
|
||||
rv_ireg_a3,
|
||||
rv_ireg_a4,
|
||||
rv_ireg_a5,
|
||||
rv_ireg_a6,
|
||||
rv_ireg_a7,
|
||||
rv_ireg_s2,
|
||||
rv_ireg_s3,
|
||||
rv_ireg_s4,
|
||||
rv_ireg_s5,
|
||||
rv_ireg_s6,
|
||||
rv_ireg_s7,
|
||||
rv_ireg_s8,
|
||||
rv_ireg_s9,
|
||||
rv_ireg_s10,
|
||||
rv_ireg_s11,
|
||||
rv_ireg_t3,
|
||||
rv_ireg_t4,
|
||||
rv_ireg_t5,
|
||||
rv_ireg_t6,
|
||||
} rv_ireg;
|
||||
|
||||
typedef enum {
|
||||
rvc_end,
|
||||
rvc_rd_eq_ra,
|
||||
rvc_rd_eq_x0,
|
||||
rvc_rs1_eq_x0,
|
||||
rvc_rs2_eq_x0,
|
||||
rvc_rs2_eq_rs1,
|
||||
rvc_rs1_eq_ra,
|
||||
rvc_imm_eq_zero,
|
||||
rvc_imm_eq_n1,
|
||||
rvc_imm_eq_p1,
|
||||
rvc_csr_eq_0x001,
|
||||
rvc_csr_eq_0x002,
|
||||
rvc_csr_eq_0x003,
|
||||
rvc_csr_eq_0xc00,
|
||||
rvc_csr_eq_0xc01,
|
||||
rvc_csr_eq_0xc02,
|
||||
rvc_csr_eq_0xc80,
|
||||
rvc_csr_eq_0xc81,
|
||||
rvc_csr_eq_0xc82,
|
||||
} rvc_constraint;
|
||||
|
||||
typedef enum {
|
||||
rv_codec_illegal,
|
||||
rv_codec_none,
|
||||
rv_codec_u,
|
||||
rv_codec_uj,
|
||||
rv_codec_i,
|
||||
rv_codec_i_sh5,
|
||||
rv_codec_i_sh6,
|
||||
rv_codec_i_sh7,
|
||||
rv_codec_i_csr,
|
||||
rv_codec_s,
|
||||
rv_codec_sb,
|
||||
rv_codec_r,
|
||||
rv_codec_r_m,
|
||||
rv_codec_r4_m,
|
||||
rv_codec_r_a,
|
||||
rv_codec_r_l,
|
||||
rv_codec_r_f,
|
||||
rv_codec_cb,
|
||||
rv_codec_cb_imm,
|
||||
rv_codec_cb_sh5,
|
||||
rv_codec_cb_sh6,
|
||||
rv_codec_ci,
|
||||
rv_codec_ci_sh5,
|
||||
rv_codec_ci_sh6,
|
||||
rv_codec_ci_16sp,
|
||||
rv_codec_ci_lwsp,
|
||||
rv_codec_ci_ldsp,
|
||||
rv_codec_ci_lqsp,
|
||||
rv_codec_ci_li,
|
||||
rv_codec_ci_lui,
|
||||
rv_codec_ci_none,
|
||||
rv_codec_ciw_4spn,
|
||||
rv_codec_cj,
|
||||
rv_codec_cj_jal,
|
||||
rv_codec_cl_lw,
|
||||
rv_codec_cl_ld,
|
||||
rv_codec_cl_lq,
|
||||
rv_codec_cr,
|
||||
rv_codec_cr_mv,
|
||||
rv_codec_cr_jalr,
|
||||
rv_codec_cr_jr,
|
||||
rv_codec_cs,
|
||||
rv_codec_cs_sw,
|
||||
rv_codec_cs_sd,
|
||||
rv_codec_cs_sq,
|
||||
rv_codec_css_swsp,
|
||||
rv_codec_css_sdsp,
|
||||
rv_codec_css_sqsp,
|
||||
rv_codec_k_bs,
|
||||
rv_codec_k_rnum,
|
||||
rv_codec_v_r,
|
||||
rv_codec_v_ldst,
|
||||
rv_codec_v_i,
|
||||
rv_codec_vsetvli,
|
||||
rv_codec_vsetivli,
|
||||
rv_codec_zcb_ext,
|
||||
rv_codec_zcb_mul,
|
||||
rv_codec_zcb_lb,
|
||||
rv_codec_zcb_lh,
|
||||
rv_codec_zcmp_cm_pushpop,
|
||||
rv_codec_zcmp_cm_mv,
|
||||
rv_codec_zcmt_jt,
|
||||
rv_codec_r2_imm5,
|
||||
rv_codec_r2,
|
||||
rv_codec_r2_imm6,
|
||||
rv_codec_r_imm2,
|
||||
rv_codec_r2_immhl,
|
||||
rv_codec_r2_imm2_imm5,
|
||||
rv_codec_fli,
|
||||
} rv_codec;
|
||||
|
||||
/* structures */
|
||||
|
||||
typedef struct {
|
||||
const int op;
|
||||
const rvc_constraint *constraints;
|
||||
} rv_comp_data;
|
||||
|
||||
typedef struct {
|
||||
const char * const name;
|
||||
const rv_codec codec;
|
||||
const char * const format;
|
||||
const rv_comp_data *pseudo;
|
||||
const short decomp_rv32;
|
||||
const short decomp_rv64;
|
||||
const short decomp_rv128;
|
||||
const short decomp_data;
|
||||
} rv_opcode_data;
|
||||
|
||||
typedef struct {
|
||||
RISCVCPUConfig *cfg;
|
||||
uint64_t pc;
|
||||
uint64_t inst;
|
||||
const rv_opcode_data *opcode_data;
|
||||
int32_t imm;
|
||||
int32_t imm1;
|
||||
uint16_t op;
|
||||
uint8_t codec;
|
||||
uint8_t rd;
|
||||
uint8_t rs1;
|
||||
uint8_t rs2;
|
||||
uint8_t rs3;
|
||||
uint8_t rm;
|
||||
uint8_t pred;
|
||||
uint8_t succ;
|
||||
uint8_t aq;
|
||||
uint8_t rl;
|
||||
uint8_t bs;
|
||||
uint8_t rnum;
|
||||
uint8_t vm;
|
||||
uint32_t vzimm;
|
||||
uint8_t rlist;
|
||||
} rv_decode;
|
||||
|
||||
enum {
|
||||
rv_op_illegal = 0
|
||||
};
|
||||
|
||||
enum {
|
||||
rvcd_imm_nz = 0x1
|
||||
};
|
||||
|
||||
/* instruction formats */
|
||||
|
||||
#define rv_fmt_none "O\t"
|
||||
#define rv_fmt_rs1 "O\t1"
|
||||
#define rv_fmt_offset "O\to"
|
||||
#define rv_fmt_pred_succ "O\tp,s"
|
||||
#define rv_fmt_rs1_rs2 "O\t1,2"
|
||||
#define rv_fmt_rd_imm "O\t0,i"
|
||||
#define rv_fmt_rd_offset "O\t0,o"
|
||||
#define rv_fmt_rd_rs1_rs2 "O\t0,1,2"
|
||||
#define rv_fmt_frd_rs1 "O\t3,1"
|
||||
#define rv_fmt_frd_rs1_rs2 "O\t3,1,2"
|
||||
#define rv_fmt_frd_frs1 "O\t3,4"
|
||||
#define rv_fmt_rd_frs1 "O\t0,4"
|
||||
#define rv_fmt_rd_frs1_frs2 "O\t0,4,5"
|
||||
#define rv_fmt_frd_frs1_frs2 "O\t3,4,5"
|
||||
#define rv_fmt_rm_frd_frs1 "O\tr,3,4"
|
||||
#define rv_fmt_rm_frd_rs1 "O\tr,3,1"
|
||||
#define rv_fmt_rm_rd_frs1 "O\tr,0,4"
|
||||
#define rv_fmt_rm_frd_frs1_frs2 "O\tr,3,4,5"
|
||||
#define rv_fmt_rm_frd_frs1_frs2_frs3 "O\tr,3,4,5,6"
|
||||
#define rv_fmt_rd_rs1_imm "O\t0,1,i"
|
||||
#define rv_fmt_rd_rs1_offset "O\t0,1,i"
|
||||
#define rv_fmt_rd_offset_rs1 "O\t0,i(1)"
|
||||
#define rv_fmt_frd_offset_rs1 "O\t3,i(1)"
|
||||
#define rv_fmt_rd_csr_rs1 "O\t0,c,1"
|
||||
#define rv_fmt_rd_csr_zimm "O\t0,c,7"
|
||||
#define rv_fmt_rs2_offset_rs1 "O\t2,i(1)"
|
||||
#define rv_fmt_frs2_offset_rs1 "O\t5,i(1)"
|
||||
#define rv_fmt_rs1_rs2_offset "O\t1,2,o"
|
||||
#define rv_fmt_rs2_rs1_offset "O\t2,1,o"
|
||||
#define rv_fmt_aqrl_rd_rs2_rs1 "OAR\t0,2,(1)"
|
||||
#define rv_fmt_aqrl_rd_rs1 "OAR\t0,(1)"
|
||||
#define rv_fmt_rd "O\t0"
|
||||
#define rv_fmt_rd_zimm "O\t0,7"
|
||||
#define rv_fmt_rd_rs1 "O\t0,1"
|
||||
#define rv_fmt_rd_rs2 "O\t0,2"
|
||||
#define rv_fmt_rs1_offset "O\t1,o"
|
||||
#define rv_fmt_rs2_offset "O\t2,o"
|
||||
#define rv_fmt_rs1_rs2_bs "O\t1,2,b"
|
||||
#define rv_fmt_rd_rs1_rnum "O\t0,1,n"
|
||||
#define rv_fmt_ldst_vd_rs1_vm "O\tD,(1)m"
|
||||
#define rv_fmt_ldst_vd_rs1_rs2_vm "O\tD,(1),2m"
|
||||
#define rv_fmt_ldst_vd_rs1_vs2_vm "O\tD,(1),Fm"
|
||||
#define rv_fmt_vd_vs2_vs1 "O\tD,F,E"
|
||||
#define rv_fmt_vd_vs2_vs1_vl "O\tD,F,El"
|
||||
#define rv_fmt_vd_vs2_vs1_vm "O\tD,F,Em"
|
||||
#define rv_fmt_vd_vs2_rs1_vl "O\tD,F,1l"
|
||||
#define rv_fmt_vd_vs2_fs1_vl "O\tD,F,4l"
|
||||
#define rv_fmt_vd_vs2_rs1_vm "O\tD,F,1m"
|
||||
#define rv_fmt_vd_vs2_fs1_vm "O\tD,F,4m"
|
||||
#define rv_fmt_vd_vs2_imm_vl "O\tD,F,il"
|
||||
#define rv_fmt_vd_vs2_imm_vm "O\tD,F,im"
|
||||
#define rv_fmt_vd_vs2_uimm_vm "O\tD,F,um"
|
||||
#define rv_fmt_vd_vs1_vs2_vm "O\tD,E,Fm"
|
||||
#define rv_fmt_vd_rs1_vs2_vm "O\tD,1,Fm"
|
||||
#define rv_fmt_vd_fs1_vs2_vm "O\tD,4,Fm"
|
||||
#define rv_fmt_vd_vs1 "O\tD,E"
|
||||
#define rv_fmt_vd_rs1 "O\tD,1"
|
||||
#define rv_fmt_vd_fs1 "O\tD,4"
|
||||
#define rv_fmt_vd_imm "O\tD,i"
|
||||
#define rv_fmt_vd_vs2 "O\tD,F"
|
||||
#define rv_fmt_vd_vs2_vm "O\tD,Fm"
|
||||
#define rv_fmt_rd_vs2_vm "O\t0,Fm"
|
||||
#define rv_fmt_rd_vs2 "O\t0,F"
|
||||
#define rv_fmt_fd_vs2 "O\t3,F"
|
||||
#define rv_fmt_vd_vm "O\tDm"
|
||||
#define rv_fmt_vsetvli "O\t0,1,v"
|
||||
#define rv_fmt_vsetivli "O\t0,u,v"
|
||||
#define rv_fmt_rs1_rs2_zce_ldst "O\t2,i(1)"
|
||||
#define rv_fmt_push_rlist "O\tx,-i"
|
||||
#define rv_fmt_pop_rlist "O\tx,i"
|
||||
#define rv_fmt_zcmt_index "O\ti"
|
||||
#define rv_fmt_rd_rs1_rs2_imm "O\t0,1,2,i"
|
||||
#define rv_fmt_frd_rs1_rs2_imm "O\t3,1,2,i"
|
||||
#define rv_fmt_rd_rs1_immh_imml "O\t0,1,i,j"
|
||||
#define rv_fmt_rd_rs1_immh_imml_addr "O\t0,(1),i,j"
|
||||
#define rv_fmt_rd2_imm "O\t0,2,(1),i"
|
||||
#define rv_fmt_fli "O\t3,h"
|
||||
|
||||
#endif /* DISAS_RISCV_H */
|
@ -93,6 +93,7 @@ The following machine-specific options are supported:
|
||||
|
||||
When this option is "on", ACLINT devices will be emulated instead of
|
||||
SiFive CLINT. When not specified, this option is assumed to be "off".
|
||||
This option is restricted to the TCG accelerator.
|
||||
|
||||
- aia=[none|aplic|aplic-imsic]
|
||||
|
||||
|
@ -158,6 +158,9 @@ config SIFIVE_TEST
|
||||
config SIFIVE_E_PRCI
|
||||
bool
|
||||
|
||||
config SIFIVE_E_AON
|
||||
bool
|
||||
|
||||
config SIFIVE_U_OTP
|
||||
bool
|
||||
|
||||
|
@ -30,6 +30,7 @@ system_ss.add(when: 'CONFIG_MCHP_PFSOC_IOSCB', if_true: files('mchp_pfsoc_ioscb.
|
||||
system_ss.add(when: 'CONFIG_MCHP_PFSOC_SYSREG', if_true: files('mchp_pfsoc_sysreg.c'))
|
||||
system_ss.add(when: 'CONFIG_SIFIVE_TEST', if_true: files('sifive_test.c'))
|
||||
system_ss.add(when: 'CONFIG_SIFIVE_E_PRCI', if_true: files('sifive_e_prci.c'))
|
||||
system_ss.add(when: 'CONFIG_SIFIVE_E_AON', if_true: files('sifive_e_aon.c'))
|
||||
system_ss.add(when: 'CONFIG_SIFIVE_U_OTP', if_true: files('sifive_u_otp.c'))
|
||||
system_ss.add(when: 'CONFIG_SIFIVE_U_PRCI', if_true: files('sifive_u_prci.c'))
|
||||
|
||||
|
319
hw/misc/sifive_e_aon.c
Normal file
319
hw/misc/sifive_e_aon.c
Normal file
@ -0,0 +1,319 @@
|
||||
/*
|
||||
* SiFive HiFive1 AON (Always On Domain) for QEMU.
|
||||
*
|
||||
* Copyright (c) 2022 SiFive, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/irq.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/misc/sifive_e_aon.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/watchdog.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
||||
REG32(AON_WDT_WDOGCFG, 0x0)
|
||||
FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4)
|
||||
FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2)
|
||||
FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14)
|
||||
FIELD(AON_WDT_WDOGCFG, IP0, 28, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3)
|
||||
REG32(AON_WDT_WDOGCOUNT, 0x8)
|
||||
FIELD(AON_WDT_WDOGCOUNT, VALUE, 0, 31)
|
||||
REG32(AON_WDT_WDOGS, 0x10)
|
||||
REG32(AON_WDT_WDOGFEED, 0x18)
|
||||
REG32(AON_WDT_WDOGKEY, 0x1c)
|
||||
REG32(AON_WDT_WDOGCMP0, 0x20)
|
||||
|
||||
static void sifive_e_aon_wdt_update_wdogcount(SiFiveEAONState *r)
|
||||
{
|
||||
int64_t now;
|
||||
if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 0 &&
|
||||
FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
r->wdogcount += muldiv64(now - r->wdog_restart_time,
|
||||
r->wdogclk_freq, NANOSECONDS_PER_SECOND);
|
||||
|
||||
/* Clean the most significant bit. */
|
||||
r->wdogcount &= R_AON_WDT_WDOGCOUNT_VALUE_MASK;
|
||||
r->wdog_restart_time = now;
|
||||
}
|
||||
|
||||
static void sifive_e_aon_wdt_update_state(SiFiveEAONState *r)
|
||||
{
|
||||
uint16_t wdogs;
|
||||
bool cmp_signal = false;
|
||||
sifive_e_aon_wdt_update_wdogcount(r);
|
||||
wdogs = (uint16_t)(r->wdogcount >>
|
||||
FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE));
|
||||
|
||||
if (wdogs >= r->wdogcmp0) {
|
||||
cmp_signal = true;
|
||||
if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, ZEROCMP) == 1) {
|
||||
r->wdogcount = 0;
|
||||
wdogs = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (cmp_signal) {
|
||||
if (FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN) == 1) {
|
||||
watchdog_perform_action();
|
||||
}
|
||||
r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, IP0, 1);
|
||||
}
|
||||
|
||||
qemu_set_irq(r->wdog_irq, FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
if (wdogs < r->wdogcmp0 &&
|
||||
(FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS) == 1 ||
|
||||
FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE) == 1)) {
|
||||
int64_t next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
next += muldiv64((r->wdogcmp0 - wdogs) <<
|
||||
FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, SCALE),
|
||||
NANOSECONDS_PER_SECOND, r->wdogclk_freq);
|
||||
timer_mod(r->wdog_timer, next);
|
||||
} else {
|
||||
timer_mod(r->wdog_timer, INT64_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback used when the timer set using timer_mod expires.
|
||||
*/
|
||||
static void sifive_e_aon_wdt_expired_cb(void *opaque)
|
||||
{
|
||||
SiFiveEAONState *r = SIFIVE_E_AON(opaque);
|
||||
sifive_e_aon_wdt_update_state(r);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
sifive_e_aon_wdt_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
SiFiveEAONState *r = SIFIVE_E_AON(opaque);
|
||||
|
||||
switch (addr) {
|
||||
case A_AON_WDT_WDOGCFG:
|
||||
return r->wdogcfg;
|
||||
case A_AON_WDT_WDOGCOUNT:
|
||||
sifive_e_aon_wdt_update_wdogcount(r);
|
||||
return r->wdogcount;
|
||||
case A_AON_WDT_WDOGS:
|
||||
sifive_e_aon_wdt_update_wdogcount(r);
|
||||
return r->wdogcount >>
|
||||
FIELD_EX32(r->wdogcfg,
|
||||
AON_WDT_WDOGCFG,
|
||||
SCALE);
|
||||
case A_AON_WDT_WDOGFEED:
|
||||
return 0;
|
||||
case A_AON_WDT_WDOGKEY:
|
||||
return r->wdogunlock;
|
||||
case A_AON_WDT_WDOGCMP0:
|
||||
return r->wdogcmp0;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
|
||||
__func__, (int)addr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sifive_e_aon_wdt_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
SiFiveEAONState *r = SIFIVE_E_AON(opaque);
|
||||
uint32_t value = val64;
|
||||
|
||||
switch (addr) {
|
||||
case A_AON_WDT_WDOGCFG: {
|
||||
uint8_t new_en_always;
|
||||
uint8_t new_en_core_awake;
|
||||
uint8_t old_en_always;
|
||||
uint8_t old_en_core_awake;
|
||||
if (r->wdogunlock == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
new_en_always = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_ALWAYS);
|
||||
new_en_core_awake = FIELD_EX32(value, AON_WDT_WDOGCFG, EN_CORE_AWAKE);
|
||||
old_en_always = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS);
|
||||
old_en_core_awake = FIELD_EX32(r->wdogcfg, AON_WDT_WDOGCFG,
|
||||
EN_CORE_AWAKE);
|
||||
|
||||
if ((old_en_always ||
|
||||
old_en_core_awake) == 1 &&
|
||||
(new_en_always ||
|
||||
new_en_core_awake) == 0) {
|
||||
sifive_e_aon_wdt_update_wdogcount(r);
|
||||
} else if ((old_en_always ||
|
||||
old_en_core_awake) == 0 &&
|
||||
(new_en_always ||
|
||||
new_en_core_awake) == 1) {
|
||||
r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
r->wdogcfg = value;
|
||||
r->wdogunlock = 0;
|
||||
break;
|
||||
}
|
||||
case A_AON_WDT_WDOGCOUNT:
|
||||
if (r->wdogunlock == 0) {
|
||||
return;
|
||||
}
|
||||
r->wdogcount = value & R_AON_WDT_WDOGCOUNT_VALUE_MASK;
|
||||
r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
r->wdogunlock = 0;
|
||||
break;
|
||||
case A_AON_WDT_WDOGS:
|
||||
return;
|
||||
case A_AON_WDT_WDOGFEED:
|
||||
if (r->wdogunlock == 0) {
|
||||
return;
|
||||
}
|
||||
if (value == SIFIVE_E_AON_WDOGFEED) {
|
||||
r->wdogcount = 0;
|
||||
r->wdog_restart_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
|
||||
}
|
||||
r->wdogunlock = 0;
|
||||
break;
|
||||
case A_AON_WDT_WDOGKEY:
|
||||
if (value == SIFIVE_E_AON_WDOGKEY) {
|
||||
r->wdogunlock = 1;
|
||||
}
|
||||
break;
|
||||
case A_AON_WDT_WDOGCMP0:
|
||||
if (r->wdogunlock == 0) {
|
||||
return;
|
||||
}
|
||||
r->wdogcmp0 = (uint16_t) value;
|
||||
r->wdogunlock = 0;
|
||||
break;
|
||||
default:
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x v=0x%x\n",
|
||||
__func__, (int)addr, (int)value);
|
||||
}
|
||||
sifive_e_aon_wdt_update_state(r);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
sifive_e_aon_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
if (addr < SIFIVE_E_AON_RTC) {
|
||||
return sifive_e_aon_wdt_read(opaque, addr, size);
|
||||
} else if (addr < SIFIVE_E_AON_MAX) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Unimplemented read: addr=0x%x\n",
|
||||
__func__, (int)addr);
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad read: addr=0x%x\n",
|
||||
__func__, (int)addr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
sifive_e_aon_write(void *opaque, hwaddr addr,
|
||||
uint64_t val64, unsigned int size)
|
||||
{
|
||||
if (addr < SIFIVE_E_AON_RTC) {
|
||||
sifive_e_aon_wdt_write(opaque, addr, val64, size);
|
||||
} else if (addr < SIFIVE_E_AON_MAX) {
|
||||
qemu_log_mask(LOG_UNIMP, "%s: Unimplemented write: addr=0x%x\n",
|
||||
__func__, (int)addr);
|
||||
} else {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: bad write: addr=0x%x\n",
|
||||
__func__, (int)addr);
|
||||
}
|
||||
}
|
||||
|
||||
static const MemoryRegionOps sifive_e_aon_ops = {
|
||||
.read = sifive_e_aon_read,
|
||||
.write = sifive_e_aon_write,
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
.impl = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
},
|
||||
.valid = {
|
||||
.min_access_size = 4,
|
||||
.max_access_size = 4
|
||||
}
|
||||
};
|
||||
|
||||
static void sifive_e_aon_reset(DeviceState *dev)
|
||||
{
|
||||
SiFiveEAONState *r = SIFIVE_E_AON(dev);
|
||||
|
||||
r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, RSTEN, 0);
|
||||
r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0);
|
||||
r->wdogcfg = FIELD_DP32(r->wdogcfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE, 0);
|
||||
r->wdogcmp0 = 0xbeef;
|
||||
|
||||
sifive_e_aon_wdt_update_state(r);
|
||||
}
|
||||
|
||||
static void sifive_e_aon_init(Object *obj)
|
||||
{
|
||||
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
|
||||
SiFiveEAONState *r = SIFIVE_E_AON(obj);
|
||||
|
||||
memory_region_init_io(&r->mmio, OBJECT(r), &sifive_e_aon_ops, r,
|
||||
TYPE_SIFIVE_E_AON, SIFIVE_E_AON_MAX);
|
||||
sysbus_init_mmio(sbd, &r->mmio);
|
||||
|
||||
/* watchdog timer */
|
||||
r->wdog_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||
sifive_e_aon_wdt_expired_cb, r);
|
||||
r->wdogclk_freq = SIFIVE_E_LFCLK_DEFAULT_FREQ;
|
||||
sysbus_init_irq(sbd, &r->wdog_irq);
|
||||
}
|
||||
|
||||
static Property sifive_e_aon_properties[] = {
|
||||
DEFINE_PROP_UINT64("wdogclk-frequency", SiFiveEAONState, wdogclk_freq,
|
||||
SIFIVE_E_LFCLK_DEFAULT_FREQ),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void sifive_e_aon_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->reset = sifive_e_aon_reset;
|
||||
device_class_set_props(dc, sifive_e_aon_properties);
|
||||
}
|
||||
|
||||
static const TypeInfo sifive_e_aon_info = {
|
||||
.name = TYPE_SIFIVE_E_AON,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(SiFiveEAONState),
|
||||
.instance_init = sifive_e_aon_init,
|
||||
.class_init = sifive_e_aon_class_init,
|
||||
};
|
||||
|
||||
static void sifive_e_aon_register_types(void)
|
||||
{
|
||||
type_register_static(&sifive_e_aon_info);
|
||||
}
|
||||
|
||||
type_init(sifive_e_aon_register_types)
|
@ -60,6 +60,7 @@ config SIFIVE_E
|
||||
select SIFIVE_PLIC
|
||||
select SIFIVE_UART
|
||||
select SIFIVE_E_PRCI
|
||||
select SIFIVE_E_AON
|
||||
select UNIMP
|
||||
|
||||
config SIFIVE_U
|
||||
|
@ -45,6 +45,7 @@
|
||||
#include "hw/intc/riscv_aclint.h"
|
||||
#include "hw/intc/sifive_plic.h"
|
||||
#include "hw/misc/sifive_e_prci.h"
|
||||
#include "hw/misc/sifive_e_aon.h"
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
|
||||
@ -185,6 +186,8 @@ static void sifive_e_soc_init(Object *obj)
|
||||
object_property_set_int(OBJECT(&s->cpus), "resetvec", 0x1004, &error_abort);
|
||||
object_initialize_child(obj, "riscv.sifive.e.gpio0", &s->gpio,
|
||||
TYPE_SIFIVE_GPIO);
|
||||
object_initialize_child(obj, "riscv.sifive.e.aon", &s->aon,
|
||||
TYPE_SIFIVE_E_AON);
|
||||
}
|
||||
|
||||
static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
@ -223,10 +226,17 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus,
|
||||
RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME,
|
||||
RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, false);
|
||||
create_unimplemented_device("riscv.sifive.e.aon",
|
||||
memmap[SIFIVE_E_DEV_AON].base, memmap[SIFIVE_E_DEV_AON].size);
|
||||
sifive_e_prci_create(memmap[SIFIVE_E_DEV_PRCI].base);
|
||||
|
||||
/* AON */
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->aon), errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Map AON registers */
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->aon), 0, memmap[SIFIVE_E_DEV_AON].base);
|
||||
|
||||
/* GPIO */
|
||||
|
||||
if (!sysbus_realize(SYS_BUS_DEVICE(&s->gpio), errp)) {
|
||||
@ -245,6 +255,9 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp)
|
||||
qdev_get_gpio_in(DEVICE(s->plic),
|
||||
SIFIVE_E_GPIO0_IRQ0 + i));
|
||||
}
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->aon), 0,
|
||||
qdev_get_gpio_in(DEVICE(s->plic),
|
||||
SIFIVE_E_AON_WDT_IRQ));
|
||||
|
||||
sifive_uart_create(sys_mem, memmap[SIFIVE_E_DEV_UART0].base,
|
||||
serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART0_IRQ));
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "chardev/char.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/tpm.h"
|
||||
#include "hw/pci/pci.h"
|
||||
@ -243,13 +244,13 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket,
|
||||
s->soc[socket].hartid_base + cpu);
|
||||
qemu_fdt_add_subnode(ms->fdt, cpu_name);
|
||||
|
||||
satp_mode_max = satp_mode_max_from_map(
|
||||
s->soc[socket].harts[cpu].cfg.satp_mode.map);
|
||||
sv_name = g_strdup_printf("riscv,%s",
|
||||
satp_mode_str(satp_mode_max, is_32_bit));
|
||||
qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name);
|
||||
g_free(sv_name);
|
||||
|
||||
if (cpu_ptr->cfg.satp_mode.supported != 0) {
|
||||
satp_mode_max = satp_mode_max_from_map(cpu_ptr->cfg.satp_mode.map);
|
||||
sv_name = g_strdup_printf("riscv,%s",
|
||||
satp_mode_str(satp_mode_max, is_32_bit));
|
||||
qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name);
|
||||
g_free(sv_name);
|
||||
}
|
||||
|
||||
name = riscv_isa_string(cpu_ptr);
|
||||
qemu_fdt_setprop_string(ms->fdt, cpu_name, "riscv,isa", name);
|
||||
@ -776,7 +777,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap,
|
||||
|
||||
g_free(clust_name);
|
||||
|
||||
if (!kvm_enabled()) {
|
||||
if (tcg_enabled()) {
|
||||
if (s->have_aclint) {
|
||||
create_fdt_socket_aclint(s, memmap, socket,
|
||||
&intc_phandles[phandle_pos]);
|
||||
@ -1244,10 +1245,21 @@ static void virt_machine_done(Notifier *notifier, void *data)
|
||||
target_ulong start_addr = memmap[VIRT_DRAM].base;
|
||||
target_ulong firmware_end_addr, kernel_start_addr;
|
||||
const char *firmware_name = riscv_default_firmware_name(&s->soc[0]);
|
||||
uint32_t fdt_load_addr;
|
||||
uint64_t fdt_load_addr;
|
||||
uint64_t kernel_entry = 0;
|
||||
BlockBackend *pflash_blk0;
|
||||
|
||||
/* load/create device tree */
|
||||
if (machine->dtb) {
|
||||
machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
|
||||
if (!machine->fdt) {
|
||||
error_report("load_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
create_fdt(s, memmap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only direct boot kernel is currently supported for KVM VM,
|
||||
* so the "-bios" parameter is not supported when KVM is enabled.
|
||||
@ -1370,7 +1382,7 @@ static void virt_machine_init(MachineState *machine)
|
||||
hart_count, &error_abort);
|
||||
sysbus_realize(SYS_BUS_DEVICE(&s->soc[i]), &error_fatal);
|
||||
|
||||
if (!kvm_enabled()) {
|
||||
if (tcg_enabled()) {
|
||||
if (s->have_aclint) {
|
||||
if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) {
|
||||
/* Per-socket ACLINT MTIMER */
|
||||
@ -1508,17 +1520,6 @@ static void virt_machine_init(MachineState *machine)
|
||||
}
|
||||
virt_flash_map(s, system_memory);
|
||||
|
||||
/* load/create device tree */
|
||||
if (machine->dtb) {
|
||||
machine->fdt = load_device_tree(machine->dtb, &s->fdt_size);
|
||||
if (!machine->fdt) {
|
||||
error_report("load_device_tree() failed");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
create_fdt(s, memmap);
|
||||
}
|
||||
|
||||
s->machine_done.notify = virt_machine_done;
|
||||
qemu_add_machine_init_done_notifier(&s->machine_done);
|
||||
}
|
||||
@ -1682,12 +1683,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data)
|
||||
machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS);
|
||||
#endif
|
||||
|
||||
object_class_property_add_bool(oc, "aclint", virt_get_aclint,
|
||||
virt_set_aclint);
|
||||
object_class_property_set_description(oc, "aclint",
|
||||
"Set on/off to enable/disable "
|
||||
"emulating ACLINT devices");
|
||||
|
||||
if (tcg_enabled()) {
|
||||
object_class_property_add_bool(oc, "aclint", virt_get_aclint,
|
||||
virt_set_aclint);
|
||||
object_class_property_set_description(oc, "aclint",
|
||||
"Set on/off to enable/disable "
|
||||
"emulating ACLINT devices");
|
||||
}
|
||||
object_class_property_add_str(oc, "aia", virt_get_aia,
|
||||
virt_set_aia);
|
||||
object_class_property_set_description(oc, "aia",
|
||||
|
60
include/hw/misc/sifive_e_aon.h
Normal file
60
include/hw/misc/sifive_e_aon.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* SiFive HiFive1 AON (Always On Domain) interface.
|
||||
*
|
||||
* Copyright (c) 2022 SiFive, Inc. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef HW_SIFIVE_AON_H
|
||||
#define HW_SIFIVE_AON_H
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_SIFIVE_E_AON "riscv.sifive.e.aon"
|
||||
OBJECT_DECLARE_SIMPLE_TYPE(SiFiveEAONState, SIFIVE_E_AON)
|
||||
|
||||
#define SIFIVE_E_AON_WDOGKEY (0x51F15E)
|
||||
#define SIFIVE_E_AON_WDOGFEED (0xD09F00D)
|
||||
#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768)
|
||||
|
||||
enum {
|
||||
SIFIVE_E_AON_WDT = 0x0,
|
||||
SIFIVE_E_AON_RTC = 0x40,
|
||||
SIFIVE_E_AON_LFROSC = 0x70,
|
||||
SIFIVE_E_AON_BACKUP = 0x80,
|
||||
SIFIVE_E_AON_PMU = 0x100,
|
||||
SIFIVE_E_AON_MAX = 0x150
|
||||
};
|
||||
|
||||
struct SiFiveEAONState {
|
||||
/*< private >*/
|
||||
SysBusDevice parent_obj;
|
||||
|
||||
/*< public >*/
|
||||
MemoryRegion mmio;
|
||||
|
||||
/*< watchdog timer >*/
|
||||
QEMUTimer *wdog_timer;
|
||||
qemu_irq wdog_irq;
|
||||
uint64_t wdog_restart_time;
|
||||
uint64_t wdogclk_freq;
|
||||
|
||||
uint32_t wdogcfg;
|
||||
uint16_t wdogcmp0;
|
||||
uint32_t wdogcount;
|
||||
uint8_t wdogunlock;
|
||||
};
|
||||
|
||||
#endif
|
@ -22,6 +22,7 @@
|
||||
#include "hw/riscv/riscv_hart.h"
|
||||
#include "hw/riscv/sifive_cpu.h"
|
||||
#include "hw/gpio/sifive_gpio.h"
|
||||
#include "hw/misc/sifive_e_aon.h"
|
||||
#include "hw/boards.h"
|
||||
|
||||
#define TYPE_RISCV_E_SOC "riscv.sifive.e.soc"
|
||||
@ -35,6 +36,7 @@ typedef struct SiFiveESoCState {
|
||||
/*< public >*/
|
||||
RISCVHartArrayState cpus;
|
||||
DeviceState *plic;
|
||||
SiFiveEAONState aon;
|
||||
SIFIVEGPIOState gpio;
|
||||
MemoryRegion xip_mem;
|
||||
MemoryRegion mask_rom;
|
||||
@ -76,9 +78,10 @@ enum {
|
||||
};
|
||||
|
||||
enum {
|
||||
SIFIVE_E_UART0_IRQ = 3,
|
||||
SIFIVE_E_UART1_IRQ = 4,
|
||||
SIFIVE_E_GPIO0_IRQ0 = 8
|
||||
SIFIVE_E_AON_WDT_IRQ = 1,
|
||||
SIFIVE_E_UART0_IRQ = 3,
|
||||
SIFIVE_E_UART1_IRQ = 4,
|
||||
SIFIVE_E_GPIO0_IRQ0 = 8
|
||||
};
|
||||
|
||||
#define SIFIVE_E_PLIC_HART_CONFIG "M"
|
||||
|
@ -228,6 +228,7 @@
|
||||
#define TARGET_NR_accept4 242
|
||||
#define TARGET_NR_arch_specific_syscall 244
|
||||
#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15)
|
||||
#define TARGET_NR_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14)
|
||||
#define TARGET_NR_prlimit64 261
|
||||
#define TARGET_NR_fanotify_init 262
|
||||
#define TARGET_NR_fanotify_mark 263
|
||||
|
@ -251,6 +251,7 @@
|
||||
#define TARGET_NR_recvmmsg 243
|
||||
#define TARGET_NR_arch_specific_syscall 244
|
||||
#define TARGET_NR_riscv_flush_icache (TARGET_NR_arch_specific_syscall + 15)
|
||||
#define TARGET_NR_riscv_hwprobe (TARGET_NR_arch_specific_syscall + 14)
|
||||
#define TARGET_NR_wait4 260
|
||||
#define TARGET_NR_prlimit64 261
|
||||
#define TARGET_NR_fanotify_init 262
|
||||
|
@ -8983,6 +8983,147 @@ static int do_getdents64(abi_long dirfd, abi_long arg2, abi_long count)
|
||||
}
|
||||
#endif /* TARGET_NR_getdents64 */
|
||||
|
||||
#if defined(TARGET_NR_riscv_hwprobe)
|
||||
|
||||
#define RISCV_HWPROBE_KEY_MVENDORID 0
|
||||
#define RISCV_HWPROBE_KEY_MARCHID 1
|
||||
#define RISCV_HWPROBE_KEY_MIMPID 2
|
||||
|
||||
#define RISCV_HWPROBE_KEY_BASE_BEHAVIOR 3
|
||||
#define RISCV_HWPROBE_BASE_BEHAVIOR_IMA (1 << 0)
|
||||
|
||||
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
|
||||
#define RISCV_HWPROBE_IMA_FD (1 << 0)
|
||||
#define RISCV_HWPROBE_IMA_C (1 << 1)
|
||||
|
||||
#define RISCV_HWPROBE_KEY_CPUPERF_0 5
|
||||
#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
|
||||
#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
|
||||
#define RISCV_HWPROBE_MISALIGNED_SLOW (2 << 0)
|
||||
#define RISCV_HWPROBE_MISALIGNED_FAST (3 << 0)
|
||||
#define RISCV_HWPROBE_MISALIGNED_UNSUPPORTED (4 << 0)
|
||||
#define RISCV_HWPROBE_MISALIGNED_MASK (7 << 0)
|
||||
|
||||
struct riscv_hwprobe {
|
||||
abi_llong key;
|
||||
abi_ullong value;
|
||||
};
|
||||
|
||||
static void risc_hwprobe_fill_pairs(CPURISCVState *env,
|
||||
struct riscv_hwprobe *pair,
|
||||
size_t pair_count)
|
||||
{
|
||||
const RISCVCPUConfig *cfg = riscv_cpu_cfg(env);
|
||||
|
||||
for (; pair_count > 0; pair_count--, pair++) {
|
||||
abi_llong key;
|
||||
abi_ullong value;
|
||||
__put_user(0, &pair->value);
|
||||
__get_user(key, &pair->key);
|
||||
switch (key) {
|
||||
case RISCV_HWPROBE_KEY_MVENDORID:
|
||||
__put_user(cfg->mvendorid, &pair->value);
|
||||
break;
|
||||
case RISCV_HWPROBE_KEY_MARCHID:
|
||||
__put_user(cfg->marchid, &pair->value);
|
||||
break;
|
||||
case RISCV_HWPROBE_KEY_MIMPID:
|
||||
__put_user(cfg->mimpid, &pair->value);
|
||||
break;
|
||||
case RISCV_HWPROBE_KEY_BASE_BEHAVIOR:
|
||||
value = riscv_has_ext(env, RVI) &&
|
||||
riscv_has_ext(env, RVM) &&
|
||||
riscv_has_ext(env, RVA) ?
|
||||
RISCV_HWPROBE_BASE_BEHAVIOR_IMA : 0;
|
||||
__put_user(value, &pair->value);
|
||||
break;
|
||||
case RISCV_HWPROBE_KEY_IMA_EXT_0:
|
||||
value = riscv_has_ext(env, RVF) &&
|
||||
riscv_has_ext(env, RVD) ?
|
||||
RISCV_HWPROBE_IMA_FD : 0;
|
||||
value |= riscv_has_ext(env, RVC) ?
|
||||
RISCV_HWPROBE_IMA_C : pair->value;
|
||||
__put_user(value, &pair->value);
|
||||
break;
|
||||
case RISCV_HWPROBE_KEY_CPUPERF_0:
|
||||
__put_user(RISCV_HWPROBE_MISALIGNED_FAST, &pair->value);
|
||||
break;
|
||||
default:
|
||||
__put_user(-1, &pair->key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int cpu_set_valid(abi_long arg3, abi_long arg4)
|
||||
{
|
||||
int ret, i, tmp;
|
||||
size_t host_mask_size, target_mask_size;
|
||||
unsigned long *host_mask;
|
||||
|
||||
/*
|
||||
* cpu_set_t represent CPU masks as bit masks of type unsigned long *.
|
||||
* arg3 contains the cpu count.
|
||||
*/
|
||||
tmp = (8 * sizeof(abi_ulong));
|
||||
target_mask_size = ((arg3 + tmp - 1) / tmp) * sizeof(abi_ulong);
|
||||
host_mask_size = (target_mask_size + (sizeof(*host_mask) - 1)) &
|
||||
~(sizeof(*host_mask) - 1);
|
||||
|
||||
host_mask = alloca(host_mask_size);
|
||||
|
||||
ret = target_to_host_cpu_mask(host_mask, host_mask_size,
|
||||
arg4, target_mask_size);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0 ; i < host_mask_size / sizeof(*host_mask); i++) {
|
||||
if (host_mask[i] != 0) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
static abi_long do_riscv_hwprobe(CPUArchState *cpu_env, abi_long arg1,
|
||||
abi_long arg2, abi_long arg3,
|
||||
abi_long arg4, abi_long arg5)
|
||||
{
|
||||
int ret;
|
||||
struct riscv_hwprobe *host_pairs;
|
||||
|
||||
/* flags must be 0 */
|
||||
if (arg5 != 0) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
/* check cpu_set */
|
||||
if (arg3 != 0) {
|
||||
ret = cpu_set_valid(arg3, arg4);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
} else if (arg4 != 0) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
/* no pairs */
|
||||
if (arg2 == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
host_pairs = lock_user(VERIFY_WRITE, arg1,
|
||||
sizeof(*host_pairs) * (size_t)arg2, 0);
|
||||
if (host_pairs == NULL) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
risc_hwprobe_fill_pairs(cpu_env, host_pairs, arg2);
|
||||
unlock_user(host_pairs, arg1, sizeof(*host_pairs) * (size_t)arg2);
|
||||
return 0;
|
||||
}
|
||||
#endif /* TARGET_NR_riscv_hwprobe */
|
||||
|
||||
#if defined(TARGET_NR_pivot_root) && defined(__NR_pivot_root)
|
||||
_syscall2(int, pivot_root, const char *, new_root, const char *, put_old)
|
||||
#endif
|
||||
@ -13665,6 +13806,11 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1,
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_NR_riscv_hwprobe)
|
||||
case TARGET_NR_riscv_hwprobe:
|
||||
return do_riscv_hwprobe(cpu_env, arg1, arg2, arg3, arg4, arg5);
|
||||
#endif
|
||||
|
||||
default:
|
||||
qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num);
|
||||
return -TARGET_ENOSYS;
|
||||
|
15
meson.build
15
meson.build
@ -55,16 +55,11 @@ qapi_trace_events = []
|
||||
|
||||
bsd_oses = ['gnu/kfreebsd', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'darwin']
|
||||
supported_oses = ['windows', 'freebsd', 'netbsd', 'openbsd', 'darwin', 'sunos', 'linux']
|
||||
supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv', 'x86', 'x86_64',
|
||||
supported_cpus = ['ppc', 'ppc64', 's390x', 'riscv32', 'riscv64', 'x86', 'x86_64',
|
||||
'arm', 'aarch64', 'loongarch64', 'mips', 'mips64', 'sparc64']
|
||||
|
||||
cpu = host_machine.cpu_family()
|
||||
|
||||
# Unify riscv* to a single family.
|
||||
if cpu in ['riscv32', 'riscv64']
|
||||
cpu = 'riscv'
|
||||
endif
|
||||
|
||||
target_dirs = config_host['TARGET_DIRS'].split()
|
||||
have_linux_user = false
|
||||
have_bsd_user = false
|
||||
@ -99,6 +94,8 @@ elif cpu == 'x86'
|
||||
host_arch = 'i386'
|
||||
elif cpu == 'mips64'
|
||||
host_arch = 'mips'
|
||||
elif cpu in ['riscv32', 'riscv64']
|
||||
host_arch = 'riscv'
|
||||
else
|
||||
host_arch = cpu
|
||||
endif
|
||||
@ -113,8 +110,10 @@ elif cpu in ['ppc', 'ppc64']
|
||||
kvm_targets = ['ppc-softmmu', 'ppc64-softmmu']
|
||||
elif cpu in ['mips', 'mips64']
|
||||
kvm_targets = ['mips-softmmu', 'mipsel-softmmu', 'mips64-softmmu', 'mips64el-softmmu']
|
||||
elif cpu in ['riscv']
|
||||
kvm_targets = ['riscv32-softmmu', 'riscv64-softmmu']
|
||||
elif cpu in ['riscv32']
|
||||
kvm_targets = ['riscv32-softmmu']
|
||||
elif cpu in ['riscv64']
|
||||
kvm_targets = ['riscv64-softmmu']
|
||||
else
|
||||
kvm_targets = []
|
||||
endif
|
||||
|
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit 6b5188ca14e59ce7bf71afe4e7d3d557c3d31bf8
|
||||
Subproject commit 2552799a1df30a3dcd2321a8b75d61d06f5fb9fc
|
@ -34,16 +34,11 @@
|
||||
#include "migration/vmstate.h"
|
||||
#include "fpu/softfloat-helpers.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "kvm_riscv.h"
|
||||
#include "tcg/tcg.h"
|
||||
|
||||
/* RISC-V CPU definitions */
|
||||
|
||||
#define RISCV_CPU_MARCHID ((QEMU_VERSION_MAJOR << 16) | \
|
||||
(QEMU_VERSION_MINOR << 8) | \
|
||||
(QEMU_VERSION_MICRO))
|
||||
#define RISCV_CPU_MIMPID RISCV_CPU_MARCHID
|
||||
|
||||
static const char riscv_single_letter_exts[] = "IEMAFDQCPVH";
|
||||
|
||||
struct isa_ext_data {
|
||||
@ -55,6 +50,17 @@ struct isa_ext_data {
|
||||
#define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \
|
||||
{#_name, _min_ver, offsetof(struct RISCVCPUConfig, _prop)}
|
||||
|
||||
/*
|
||||
* From vector_helper.c
|
||||
* Note that vector data is stored in host-endian 64-bit chunks,
|
||||
* so addressing bytes needs a host-endian fixup.
|
||||
*/
|
||||
#if HOST_BIG_ENDIAN
|
||||
#define BYTE(x) ((x) ^ 7)
|
||||
#else
|
||||
#define BYTE(x) (x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Here are the ordering rules of extension naming defined by RISC-V
|
||||
* specification :
|
||||
@ -83,6 +89,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
|
||||
ISA_EXT_DATA_ENTRY(zifencei, PRIV_VERSION_1_10_0, ext_ifencei),
|
||||
ISA_EXT_DATA_ENTRY(zihintpause, PRIV_VERSION_1_10_0, ext_zihintpause),
|
||||
ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs),
|
||||
ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa),
|
||||
ISA_EXT_DATA_ENTRY(zfbfmin, PRIV_VERSION_1_12_0, ext_zfbfmin),
|
||||
ISA_EXT_DATA_ENTRY(zfh, PRIV_VERSION_1_11_0, ext_zfh),
|
||||
ISA_EXT_DATA_ENTRY(zfhmin, PRIV_VERSION_1_11_0, ext_zfhmin),
|
||||
ISA_EXT_DATA_ENTRY(zfinx, PRIV_VERSION_1_12_0, ext_zfinx),
|
||||
@ -114,6 +122,8 @@ static const struct isa_ext_data isa_edata_arr[] = {
|
||||
ISA_EXT_DATA_ENTRY(zve32f, PRIV_VERSION_1_10_0, ext_zve32f),
|
||||
ISA_EXT_DATA_ENTRY(zve64f, PRIV_VERSION_1_10_0, ext_zve64f),
|
||||
ISA_EXT_DATA_ENTRY(zve64d, PRIV_VERSION_1_10_0, ext_zve64d),
|
||||
ISA_EXT_DATA_ENTRY(zvfbfmin, PRIV_VERSION_1_12_0, ext_zvfbfmin),
|
||||
ISA_EXT_DATA_ENTRY(zvfbfwma, PRIV_VERSION_1_12_0, ext_zvfbfwma),
|
||||
ISA_EXT_DATA_ENTRY(zvfh, PRIV_VERSION_1_12_0, ext_zvfh),
|
||||
ISA_EXT_DATA_ENTRY(zvfhmin, PRIV_VERSION_1_12_0, ext_zvfhmin),
|
||||
ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx),
|
||||
@ -183,6 +193,14 @@ const char * const riscv_fpr_regnames[] = {
|
||||
"f30/ft10", "f31/ft11"
|
||||
};
|
||||
|
||||
const char * const riscv_rvv_regnames[] = {
|
||||
"v0", "v1", "v2", "v3", "v4", "v5", "v6",
|
||||
"v7", "v8", "v9", "v10", "v11", "v12", "v13",
|
||||
"v14", "v15", "v16", "v17", "v18", "v19", "v20",
|
||||
"v21", "v22", "v23", "v24", "v25", "v26", "v27",
|
||||
"v28", "v29", "v30", "v31"
|
||||
};
|
||||
|
||||
static const char * const riscv_excp_names[] = {
|
||||
"misaligned_fetch",
|
||||
"fault_fetch",
|
||||
@ -412,6 +430,7 @@ static void rv64_thead_c906_cpu_init(Object *obj)
|
||||
set_misa(env, MXL_RV64, RVG | RVC | RVS | RVU);
|
||||
env->priv_ver = PRIV_VERSION_1_11_0;
|
||||
|
||||
cpu->cfg.ext_zfa = true;
|
||||
cpu->cfg.ext_zfh = true;
|
||||
cpu->cfg.mmu = true;
|
||||
cpu->cfg.ext_xtheadba = true;
|
||||
@ -444,6 +463,9 @@ static void rv64_veyron_v1_cpu_init(Object *obj)
|
||||
|
||||
/* Enable ISA extensions */
|
||||
cpu->cfg.mmu = true;
|
||||
cpu->cfg.ext_ifencei = true;
|
||||
cpu->cfg.ext_icsr = true;
|
||||
cpu->cfg.pmp = true;
|
||||
cpu->cfg.ext_icbom = true;
|
||||
cpu->cfg.cbom_blocksize = 64;
|
||||
cpu->cfg.cboz_blocksize = 64;
|
||||
@ -608,7 +630,8 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
int i;
|
||||
int i, j;
|
||||
uint8_t *p;
|
||||
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
if (riscv_has_ext(env, RVH)) {
|
||||
@ -692,6 +715,41 @@ static void riscv_cpu_dump_state(CPUState *cs, FILE *f, int flags)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (riscv_has_ext(env, RVV) && (flags & CPU_DUMP_VPU)) {
|
||||
static const int dump_rvv_csrs[] = {
|
||||
CSR_VSTART,
|
||||
CSR_VXSAT,
|
||||
CSR_VXRM,
|
||||
CSR_VCSR,
|
||||
CSR_VL,
|
||||
CSR_VTYPE,
|
||||
CSR_VLENB,
|
||||
};
|
||||
for (int i = 0; i < ARRAY_SIZE(dump_rvv_csrs); ++i) {
|
||||
int csrno = dump_rvv_csrs[i];
|
||||
target_ulong val = 0;
|
||||
RISCVException res = riscv_csrrw_debug(env, csrno, &val, 0, 0);
|
||||
|
||||
/*
|
||||
* Rely on the smode, hmode, etc, predicates within csr.c
|
||||
* to do the filtering of the registers that are present.
|
||||
*/
|
||||
if (res == RISCV_EXCP_NONE) {
|
||||
qemu_fprintf(f, " %-8s " TARGET_FMT_lx "\n",
|
||||
csr_ops[csrno].name, val);
|
||||
}
|
||||
}
|
||||
uint16_t vlenb = cpu->cfg.vlen >> 3;
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
qemu_fprintf(f, " %-8s ", riscv_rvv_regnames[i]);
|
||||
p = (uint8_t *)env->vreg;
|
||||
for (j = vlenb - 1 ; j >= 0; j--) {
|
||||
qemu_fprintf(f, "%02x", *(p + i * vlenb + BYTE(j)));
|
||||
}
|
||||
qemu_fprintf(f, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_set_pc(CPUState *cs, vaddr value)
|
||||
@ -858,9 +916,10 @@ static void riscv_cpu_reset_hold(Object *obj)
|
||||
static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(s);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
info->target_info = &cpu->cfg;
|
||||
|
||||
switch (riscv_cpu_mxl(&cpu->env)) {
|
||||
switch (env->xl) {
|
||||
case MXL_RV32:
|
||||
info->print_insn = print_insn_riscv32;
|
||||
break;
|
||||
@ -1050,6 +1109,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zfa && !riscv_has_ext(env, RVF)) {
|
||||
error_setg(errp, "Zfa extension requires F extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zfh) {
|
||||
cpu->cfg.ext_zfhmin = true;
|
||||
}
|
||||
@ -1059,6 +1123,11 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zfbfmin && !riscv_has_ext(env, RVF)) {
|
||||
error_setg(errp, "Zfbfmin extension depends on F extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (riscv_has_ext(env, RVD) && !riscv_has_ext(env, RVF)) {
|
||||
error_setg(errp, "D extension requires F extension");
|
||||
return;
|
||||
@ -1109,6 +1178,21 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp)
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zfbfmin) {
|
||||
error_setg(errp, "Zvfbfmin extension depends on Zfbfmin extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zvfbfmin && !cpu->cfg.ext_zve32f) {
|
||||
error_setg(errp, "Zvfbfmin extension depends on Zve32f extension");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_zvfbfwma && !cpu->cfg.ext_zvfbfmin) {
|
||||
error_setg(errp, "Zvfbfwma extension depends on Zvfbfmin extension");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the ISA extensions, checks should have happened above */
|
||||
if (cpu->cfg.ext_zhinx) {
|
||||
cpu->cfg.ext_zhinxmin = true;
|
||||
@ -1304,20 +1388,12 @@ static void riscv_cpu_validate_misa_priv(CPURISCVState *env, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
static void riscv_cpu_realize_tcg(DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
RISCVCPU *cpu = RISCV_CPU(dev);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
cpu_exec_realizefn(cs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
riscv_cpu_validate_misa_mxl(cpu, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
@ -1352,7 +1428,7 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
cs->tcg_cflags |= CF_PCREL;
|
||||
CPU(dev)->tcg_cflags |= CF_PCREL;
|
||||
|
||||
if (cpu->cfg.ext_sstc) {
|
||||
riscv_timer_init(cpu);
|
||||
@ -1365,6 +1441,28 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void riscv_cpu_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
CPUState *cs = CPU(dev);
|
||||
RISCVCPU *cpu = RISCV_CPU(dev);
|
||||
RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(dev);
|
||||
Error *local_err = NULL;
|
||||
|
||||
cpu_exec_realizefn(cs, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tcg_enabled()) {
|
||||
riscv_cpu_realize_tcg(dev, &local_err);
|
||||
if (local_err != NULL) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
riscv_cpu_finalize_features(cpu, &local_err);
|
||||
if (local_err != NULL) {
|
||||
@ -1545,33 +1643,83 @@ static void cpu_get_misa_ext_cfg(Object *obj, Visitor *v, const char *name,
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static const RISCVCPUMisaExtConfig misa_ext_cfgs[] = {
|
||||
{.name = "a", .description = "Atomic instructions",
|
||||
.misa_bit = RVA, .enabled = true},
|
||||
{.name = "c", .description = "Compressed instructions",
|
||||
.misa_bit = RVC, .enabled = true},
|
||||
{.name = "d", .description = "Double-precision float point",
|
||||
.misa_bit = RVD, .enabled = true},
|
||||
{.name = "f", .description = "Single-precision float point",
|
||||
.misa_bit = RVF, .enabled = true},
|
||||
{.name = "i", .description = "Base integer instruction set",
|
||||
.misa_bit = RVI, .enabled = true},
|
||||
{.name = "e", .description = "Base integer instruction set (embedded)",
|
||||
.misa_bit = RVE, .enabled = false},
|
||||
{.name = "m", .description = "Integer multiplication and division",
|
||||
.misa_bit = RVM, .enabled = true},
|
||||
{.name = "s", .description = "Supervisor-level instructions",
|
||||
.misa_bit = RVS, .enabled = true},
|
||||
{.name = "u", .description = "User-level instructions",
|
||||
.misa_bit = RVU, .enabled = true},
|
||||
{.name = "h", .description = "Hypervisor",
|
||||
.misa_bit = RVH, .enabled = true},
|
||||
{.name = "x-j", .description = "Dynamic translated languages",
|
||||
.misa_bit = RVJ, .enabled = false},
|
||||
{.name = "v", .description = "Vector operations",
|
||||
.misa_bit = RVV, .enabled = false},
|
||||
{.name = "g", .description = "General purpose (IMAFD_Zicsr_Zifencei)",
|
||||
.misa_bit = RVG, .enabled = false},
|
||||
typedef struct misa_ext_info {
|
||||
const char *name;
|
||||
const char *description;
|
||||
} MISAExtInfo;
|
||||
|
||||
#define MISA_INFO_IDX(_bit) \
|
||||
__builtin_ctz(_bit)
|
||||
|
||||
#define MISA_EXT_INFO(_bit, _propname, _descr) \
|
||||
[MISA_INFO_IDX(_bit)] = {.name = _propname, .description = _descr}
|
||||
|
||||
static const MISAExtInfo misa_ext_info_arr[] = {
|
||||
MISA_EXT_INFO(RVA, "a", "Atomic instructions"),
|
||||
MISA_EXT_INFO(RVC, "c", "Compressed instructions"),
|
||||
MISA_EXT_INFO(RVD, "d", "Double-precision float point"),
|
||||
MISA_EXT_INFO(RVF, "f", "Single-precision float point"),
|
||||
MISA_EXT_INFO(RVI, "i", "Base integer instruction set"),
|
||||
MISA_EXT_INFO(RVE, "e", "Base integer instruction set (embedded)"),
|
||||
MISA_EXT_INFO(RVM, "m", "Integer multiplication and division"),
|
||||
MISA_EXT_INFO(RVS, "s", "Supervisor-level instructions"),
|
||||
MISA_EXT_INFO(RVU, "u", "User-level instructions"),
|
||||
MISA_EXT_INFO(RVH, "h", "Hypervisor"),
|
||||
MISA_EXT_INFO(RVJ, "x-j", "Dynamic translated languages"),
|
||||
MISA_EXT_INFO(RVV, "v", "Vector operations"),
|
||||
MISA_EXT_INFO(RVG, "g", "General purpose (IMAFD_Zicsr_Zifencei)"),
|
||||
};
|
||||
|
||||
static int riscv_validate_misa_info_idx(uint32_t bit)
|
||||
{
|
||||
int idx;
|
||||
|
||||
/*
|
||||
* Our lowest valid input (RVA) is 1 and
|
||||
* __builtin_ctz() is UB with zero.
|
||||
*/
|
||||
g_assert(bit != 0);
|
||||
idx = MISA_INFO_IDX(bit);
|
||||
|
||||
g_assert(idx < ARRAY_SIZE(misa_ext_info_arr));
|
||||
return idx;
|
||||
}
|
||||
|
||||
const char *riscv_get_misa_ext_name(uint32_t bit)
|
||||
{
|
||||
int idx = riscv_validate_misa_info_idx(bit);
|
||||
const char *val = misa_ext_info_arr[idx].name;
|
||||
|
||||
g_assert(val != NULL);
|
||||
return val;
|
||||
}
|
||||
|
||||
const char *riscv_get_misa_ext_description(uint32_t bit)
|
||||
{
|
||||
int idx = riscv_validate_misa_info_idx(bit);
|
||||
const char *val = misa_ext_info_arr[idx].description;
|
||||
|
||||
g_assert(val != NULL);
|
||||
return val;
|
||||
}
|
||||
|
||||
#define MISA_CFG(_bit, _enabled) \
|
||||
{.misa_bit = _bit, .enabled = _enabled}
|
||||
|
||||
static RISCVCPUMisaExtConfig misa_ext_cfgs[] = {
|
||||
MISA_CFG(RVA, true),
|
||||
MISA_CFG(RVC, true),
|
||||
MISA_CFG(RVD, true),
|
||||
MISA_CFG(RVF, true),
|
||||
MISA_CFG(RVI, true),
|
||||
MISA_CFG(RVE, false),
|
||||
MISA_CFG(RVM, true),
|
||||
MISA_CFG(RVS, true),
|
||||
MISA_CFG(RVU, true),
|
||||
MISA_CFG(RVH, true),
|
||||
MISA_CFG(RVJ, false),
|
||||
MISA_CFG(RVV, false),
|
||||
MISA_CFG(RVG, false),
|
||||
};
|
||||
|
||||
static void riscv_cpu_add_misa_properties(Object *cpu_obj)
|
||||
@ -1579,7 +1727,16 @@ static void riscv_cpu_add_misa_properties(Object *cpu_obj)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(misa_ext_cfgs); i++) {
|
||||
const RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
|
||||
RISCVCPUMisaExtConfig *misa_cfg = &misa_ext_cfgs[i];
|
||||
int bit = misa_cfg->misa_bit;
|
||||
|
||||
misa_cfg->name = riscv_get_misa_ext_name(bit);
|
||||
misa_cfg->description = riscv_get_misa_ext_description(bit);
|
||||
|
||||
/* Check if KVM already created the property */
|
||||
if (object_property_find(cpu_obj, misa_cfg->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
object_property_add(cpu_obj, misa_cfg->name, "bool",
|
||||
cpu_get_misa_ext_cfg,
|
||||
@ -1600,6 +1757,7 @@ static Property riscv_cpu_extensions[] = {
|
||||
DEFINE_PROP_BOOL("Zicsr", RISCVCPU, cfg.ext_icsr, true),
|
||||
DEFINE_PROP_BOOL("Zihintpause", RISCVCPU, cfg.ext_zihintpause, true),
|
||||
DEFINE_PROP_BOOL("Zawrs", RISCVCPU, cfg.ext_zawrs, true),
|
||||
DEFINE_PROP_BOOL("Zfa", RISCVCPU, cfg.ext_zfa, true),
|
||||
DEFINE_PROP_BOOL("Zfh", RISCVCPU, cfg.ext_zfh, false),
|
||||
DEFINE_PROP_BOOL("Zfhmin", RISCVCPU, cfg.ext_zfhmin, false),
|
||||
DEFINE_PROP_BOOL("Zve32f", RISCVCPU, cfg.ext_zve32f, false),
|
||||
@ -1683,9 +1841,33 @@ static Property riscv_cpu_extensions[] = {
|
||||
DEFINE_PROP_BOOL("x-zvfh", RISCVCPU, cfg.ext_zvfh, false),
|
||||
DEFINE_PROP_BOOL("x-zvfhmin", RISCVCPU, cfg.ext_zvfhmin, false),
|
||||
|
||||
DEFINE_PROP_BOOL("x-zfbfmin", RISCVCPU, cfg.ext_zfbfmin, false),
|
||||
DEFINE_PROP_BOOL("x-zvfbfmin", RISCVCPU, cfg.ext_zvfbfmin, false),
|
||||
DEFINE_PROP_BOOL("x-zvfbfwma", RISCVCPU, cfg.ext_zvfbfwma, false),
|
||||
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
static void cpu_set_cfg_unavailable(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
const char *propname = opaque;
|
||||
bool value;
|
||||
|
||||
if (!visit_type_bool(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
error_setg(errp, "extension %s is not available with KVM",
|
||||
propname);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Add CPU properties with user-facing flags.
|
||||
*
|
||||
@ -1697,24 +1879,48 @@ static void riscv_cpu_add_user_properties(Object *obj)
|
||||
Property *prop;
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
riscv_add_satp_mode_properties(obj);
|
||||
|
||||
if (kvm_enabled()) {
|
||||
kvm_riscv_init_user_properties(obj);
|
||||
}
|
||||
#endif
|
||||
|
||||
riscv_cpu_add_misa_properties(obj);
|
||||
|
||||
for (prop = riscv_cpu_extensions; prop && prop->name; prop++) {
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (kvm_enabled()) {
|
||||
/* Check if KVM created the property already */
|
||||
if (object_property_find(obj, prop->name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the default to disabled for every extension
|
||||
* unknown to KVM and error out if the user attempts
|
||||
* to enable any of them.
|
||||
*
|
||||
* We're giving a pass for non-bool properties since they're
|
||||
* not related to the availability of extensions and can be
|
||||
* safely ignored as is.
|
||||
*/
|
||||
if (prop->info == &qdev_prop_bool) {
|
||||
object_property_add(obj, prop->name, "bool",
|
||||
NULL, cpu_set_cfg_unavailable,
|
||||
NULL, (void *)prop->name);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
qdev_property_add_static(dev, prop);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
riscv_add_satp_mode_properties(obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
static Property riscv_cpu_properties[] = {
|
||||
DEFINE_PROP_BOOL("debug", RISCVCPU, cfg.debug, true),
|
||||
|
||||
DEFINE_PROP_UINT32("mvendorid", RISCVCPU, cfg.mvendorid, 0),
|
||||
DEFINE_PROP_UINT64("marchid", RISCVCPU, cfg.marchid, RISCV_CPU_MARCHID),
|
||||
DEFINE_PROP_UINT64("mimpid", RISCVCPU, cfg.mimpid, RISCV_CPU_MIMPID),
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
DEFINE_PROP_UINT64("resetvec", RISCVCPU, env.resetvec, DEFAULT_RSTVEC),
|
||||
#endif
|
||||
@ -1798,6 +2004,119 @@ static const struct TCGCPUOps riscv_tcg_ops = {
|
||||
#endif /* !CONFIG_USER_ONLY */
|
||||
};
|
||||
|
||||
static bool riscv_cpu_is_dynamic(Object *cpu_obj)
|
||||
{
|
||||
return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL;
|
||||
}
|
||||
|
||||
static void cpu_set_mvendorid(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool dynamic_cpu = riscv_cpu_is_dynamic(obj);
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
uint32_t prev_val = cpu->cfg.mvendorid;
|
||||
uint32_t value;
|
||||
|
||||
if (!visit_type_uint32(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dynamic_cpu && prev_val != value) {
|
||||
error_setg(errp, "Unable to change %s mvendorid (0x%x)",
|
||||
object_get_typename(obj), prev_val);
|
||||
return;
|
||||
}
|
||||
|
||||
cpu->cfg.mvendorid = value;
|
||||
}
|
||||
|
||||
static void cpu_get_mvendorid(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool value = RISCV_CPU(obj)->cfg.mvendorid;
|
||||
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void cpu_set_mimpid(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool dynamic_cpu = riscv_cpu_is_dynamic(obj);
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
uint64_t prev_val = cpu->cfg.mimpid;
|
||||
uint64_t value;
|
||||
|
||||
if (!visit_type_uint64(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dynamic_cpu && prev_val != value) {
|
||||
error_setg(errp, "Unable to change %s mimpid (0x%" PRIu64 ")",
|
||||
object_get_typename(obj), prev_val);
|
||||
return;
|
||||
}
|
||||
|
||||
cpu->cfg.mimpid = value;
|
||||
}
|
||||
|
||||
static void cpu_get_mimpid(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool value = RISCV_CPU(obj)->cfg.mimpid;
|
||||
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void cpu_set_marchid(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool dynamic_cpu = riscv_cpu_is_dynamic(obj);
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
uint64_t prev_val = cpu->cfg.marchid;
|
||||
uint64_t value, invalid_val;
|
||||
uint32_t mxlen = 0;
|
||||
|
||||
if (!visit_type_uint64(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dynamic_cpu && prev_val != value) {
|
||||
error_setg(errp, "Unable to change %s marchid (0x%" PRIu64 ")",
|
||||
object_get_typename(obj), prev_val);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (riscv_cpu_mxl(&cpu->env)) {
|
||||
case MXL_RV32:
|
||||
mxlen = 32;
|
||||
break;
|
||||
case MXL_RV64:
|
||||
case MXL_RV128:
|
||||
mxlen = 64;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
invalid_val = 1LL << (mxlen - 1);
|
||||
|
||||
if (value == invalid_val) {
|
||||
error_setg(errp, "Unable to set marchid with MSB (%u) bit set "
|
||||
"and the remaining bits zero", mxlen);
|
||||
return;
|
||||
}
|
||||
|
||||
cpu->cfg.marchid = value;
|
||||
}
|
||||
|
||||
static void cpu_get_marchid(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
bool value = RISCV_CPU(obj)->cfg.marchid;
|
||||
|
||||
visit_type_bool(v, name, &value, errp);
|
||||
}
|
||||
|
||||
static void riscv_cpu_class_init(ObjectClass *c, void *data)
|
||||
{
|
||||
RISCVCPUClass *mcc = RISCV_CPU_CLASS(c);
|
||||
@ -1829,6 +2148,15 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data)
|
||||
cc->gdb_get_dynamic_xml = riscv_gdb_get_dynamic_xml;
|
||||
cc->tcg_ops = &riscv_tcg_ops;
|
||||
|
||||
object_class_property_add(c, "mvendorid", "uint32", cpu_get_mvendorid,
|
||||
cpu_set_mvendorid, NULL, NULL);
|
||||
|
||||
object_class_property_add(c, "mimpid", "uint64", cpu_get_mimpid,
|
||||
cpu_set_mimpid, NULL, NULL);
|
||||
|
||||
object_class_property_add(c, "marchid", "uint64", cpu_get_marchid,
|
||||
cpu_set_marchid, NULL, NULL);
|
||||
|
||||
device_class_set_props(dc, riscv_cpu_properties);
|
||||
}
|
||||
|
||||
@ -1840,8 +2168,7 @@ static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(isa_edata_arr); i++) {
|
||||
if (cpu->env.priv_ver >= isa_edata_arr[i].min_version &&
|
||||
isa_ext_is_enabled(cpu, &isa_edata_arr[i])) {
|
||||
if (isa_ext_is_enabled(cpu, &isa_edata_arr[i])) {
|
||||
new = g_strconcat(old, "_", isa_edata_arr[i].name, NULL);
|
||||
g_free(old);
|
||||
old = new;
|
||||
|
@ -41,7 +41,10 @@
|
||||
|
||||
#define RV(x) ((target_ulong)1 << (x - 'A'))
|
||||
|
||||
/* Consider updating misa_ext_cfgs[] when adding new MISA bits here */
|
||||
/*
|
||||
* Consider updating misa_ext_info_arr[] and misa_ext_cfgs[]
|
||||
* when adding new MISA bits here.
|
||||
*/
|
||||
#define RVI RV('I')
|
||||
#define RVE RV('E') /* E and I are mutually exclusive */
|
||||
#define RVM RV('M')
|
||||
@ -56,6 +59,8 @@
|
||||
#define RVJ RV('J')
|
||||
#define RVG RV('G')
|
||||
|
||||
const char *riscv_get_misa_ext_name(uint32_t bit);
|
||||
const char *riscv_get_misa_ext_description(uint32_t bit);
|
||||
|
||||
/* Privileged specification version */
|
||||
enum {
|
||||
@ -500,6 +505,7 @@ FIELD(TB_FLAGS, ITRIGGER, 22, 1)
|
||||
/* Virtual mode enabled */
|
||||
FIELD(TB_FLAGS, VIRT_ENABLED, 23, 1)
|
||||
FIELD(TB_FLAGS, PRIV, 24, 2)
|
||||
FIELD(TB_FLAGS, AXL, 26, 2)
|
||||
|
||||
#ifdef TARGET_RISCV32
|
||||
#define riscv_cpu_mxl(env) ((void)(env), MXL_RV32)
|
||||
@ -516,13 +522,20 @@ static inline const RISCVCPUConfig *riscv_cpu_cfg(CPURISCVState *env)
|
||||
return &env_archcpu(env)->cfg;
|
||||
}
|
||||
|
||||
#if defined(TARGET_RISCV32)
|
||||
#define cpu_recompute_xl(env) ((void)(env), MXL_RV32)
|
||||
#else
|
||||
static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
static inline int cpu_address_mode(CPURISCVState *env)
|
||||
{
|
||||
int mode = env->priv;
|
||||
|
||||
if (mode == PRV_M && get_field(env->mstatus, MSTATUS_MPRV)) {
|
||||
mode = get_field(env->mstatus, MSTATUS_MPP);
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static inline RISCVMXL cpu_get_xl(CPURISCVState *env, target_ulong mode)
|
||||
{
|
||||
RISCVMXL xl = env->misa_mxl;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
/*
|
||||
* When emulating a 32-bit-only cpu, use RV32.
|
||||
* When emulating a 64-bit cpu, and MXL has been reduced to RV32,
|
||||
@ -530,7 +543,7 @@ static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
|
||||
* back to RV64 for lower privs.
|
||||
*/
|
||||
if (xl != MXL_RV32) {
|
||||
switch (env->priv) {
|
||||
switch (mode) {
|
||||
case PRV_M:
|
||||
break;
|
||||
case PRV_U:
|
||||
@ -541,11 +554,38 @@ static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return xl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_RISCV32)
|
||||
#define cpu_recompute_xl(env) ((void)(env), MXL_RV32)
|
||||
#else
|
||||
static inline RISCVMXL cpu_recompute_xl(CPURISCVState *env)
|
||||
{
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
return cpu_get_xl(env, env->priv);
|
||||
#else
|
||||
return env->misa_mxl;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_RISCV32)
|
||||
#define cpu_address_xl(env) ((void)(env), MXL_RV32)
|
||||
#else
|
||||
static inline RISCVMXL cpu_address_xl(CPURISCVState *env)
|
||||
{
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
return env->xl;
|
||||
#else
|
||||
int mode = cpu_address_mode(env);
|
||||
|
||||
return cpu_get_xl(env, mode);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int riscv_cpu_xlen(CPURISCVState *env)
|
||||
{
|
||||
return 16 << env->xl;
|
||||
|
@ -75,6 +75,8 @@ struct RISCVCPUConfig {
|
||||
bool ext_svpbmt;
|
||||
bool ext_zdinx;
|
||||
bool ext_zawrs;
|
||||
bool ext_zfa;
|
||||
bool ext_zfbfmin;
|
||||
bool ext_zfh;
|
||||
bool ext_zfhmin;
|
||||
bool ext_zfinx;
|
||||
@ -84,6 +86,8 @@ struct RISCVCPUConfig {
|
||||
bool ext_zve64f;
|
||||
bool ext_zve64d;
|
||||
bool ext_zmmul;
|
||||
bool ext_zvfbfmin;
|
||||
bool ext_zvfbfwma;
|
||||
bool ext_zvfh;
|
||||
bool ext_zvfhmin;
|
||||
bool ext_smaia;
|
||||
@ -133,4 +137,41 @@ struct RISCVCPUConfig {
|
||||
};
|
||||
|
||||
typedef struct RISCVCPUConfig RISCVCPUConfig;
|
||||
|
||||
/* Helper functions to test for extensions. */
|
||||
|
||||
static inline bool always_true_p(const RISCVCPUConfig *cfg __attribute__((__unused__)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool has_xthead_p(const RISCVCPUConfig *cfg)
|
||||
{
|
||||
return cfg->ext_xtheadba || cfg->ext_xtheadbb ||
|
||||
cfg->ext_xtheadbs || cfg->ext_xtheadcmo ||
|
||||
cfg->ext_xtheadcondmov ||
|
||||
cfg->ext_xtheadfmemidx || cfg->ext_xtheadfmv ||
|
||||
cfg->ext_xtheadmac || cfg->ext_xtheadmemidx ||
|
||||
cfg->ext_xtheadmempair || cfg->ext_xtheadsync;
|
||||
}
|
||||
|
||||
#define MATERIALISE_EXT_PREDICATE(ext) \
|
||||
static inline bool has_ ## ext ## _p(const RISCVCPUConfig *cfg) \
|
||||
{ \
|
||||
return cfg->ext_ ## ext ; \
|
||||
}
|
||||
|
||||
MATERIALISE_EXT_PREDICATE(xtheadba)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadbb)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadbs)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadcmo)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadcondmov)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadfmemidx)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadfmv)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadmac)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadmemidx)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadmempair)
|
||||
MATERIALISE_EXT_PREDICATE(xtheadsync)
|
||||
MATERIALISE_EXT_PREDICATE(XVentanaCondOps)
|
||||
|
||||
#endif
|
||||
|
@ -47,7 +47,8 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch)
|
||||
|
||||
if (mode == PRV_M && get_field(status, MSTATUS_MPRV)) {
|
||||
mode = get_field(env->mstatus, MSTATUS_MPP);
|
||||
virt = get_field(env->mstatus, MSTATUS_MPV);
|
||||
virt = get_field(env->mstatus, MSTATUS_MPV) &&
|
||||
(mode != PRV_M);
|
||||
if (virt) {
|
||||
status = env->vsstatus;
|
||||
}
|
||||
@ -134,6 +135,7 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
|
||||
flags = FIELD_DP32(flags, TB_FLAGS, FS, fs);
|
||||
flags = FIELD_DP32(flags, TB_FLAGS, VS, vs);
|
||||
flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl);
|
||||
flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env));
|
||||
if (env->cur_pmmask != 0) {
|
||||
flags = FIELD_DP32(flags, TB_FLAGS, PM_MASK_ENABLED, 1);
|
||||
}
|
||||
@ -147,13 +149,16 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc,
|
||||
void riscv_cpu_update_mask(CPURISCVState *env)
|
||||
{
|
||||
target_ulong mask = 0, base = 0;
|
||||
RISCVMXL xl = env->xl;
|
||||
/*
|
||||
* TODO: Current RVJ spec does not specify
|
||||
* how the extension interacts with XLEN.
|
||||
*/
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
int mode = cpu_address_mode(env);
|
||||
xl = cpu_get_xl(env, mode);
|
||||
if (riscv_has_ext(env, RVJ)) {
|
||||
switch (env->priv) {
|
||||
switch (mode) {
|
||||
case PRV_M:
|
||||
if (env->mmte & M_PM_ENABLE) {
|
||||
mask = env->mpmmask;
|
||||
@ -177,7 +182,7 @@ void riscv_cpu_update_mask(CPURISCVState *env)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (env->xl == MXL_RV32) {
|
||||
if (xl == MXL_RV32) {
|
||||
env->cur_pmmask = mask & UINT32_MAX;
|
||||
env->cur_pmbase = base & UINT32_MAX;
|
||||
} else {
|
||||
@ -1277,7 +1282,6 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
||||
if (ret == TRANSLATE_G_STAGE_FAIL) {
|
||||
first_stage_error = false;
|
||||
two_stage_indirect_error = true;
|
||||
access_type = MMU_DATA_LOAD;
|
||||
}
|
||||
|
||||
qemu_log_mask(CPU_LOG_MMU,
|
||||
|
@ -1311,11 +1311,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
|
||||
}
|
||||
|
||||
if (xl != MXL_RV32 || env->debugger) {
|
||||
/*
|
||||
* RV32: MPV and GVA are not in mstatus. The current plan is to
|
||||
* add them to mstatush. For now, we just don't support it.
|
||||
*/
|
||||
mask |= MSTATUS_MPV | MSTATUS_GVA;
|
||||
if (riscv_has_ext(env, RVH)) {
|
||||
mask |= MSTATUS_MPV | MSTATUS_GVA;
|
||||
}
|
||||
if ((val & MSTATUS64_UXL) != 0) {
|
||||
mask |= MSTATUS64_UXL;
|
||||
}
|
||||
@ -1323,10 +1321,6 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
|
||||
|
||||
mstatus = (mstatus & ~mask) | (val & mask);
|
||||
|
||||
if (xl > MXL_RV32) {
|
||||
/* SXL field is for now read only */
|
||||
mstatus = set_field(mstatus, MSTATUS64_SXL, xl);
|
||||
}
|
||||
env->mstatus = mstatus;
|
||||
|
||||
/*
|
||||
@ -1335,8 +1329,9 @@ static RISCVException write_mstatus(CPURISCVState *env, int csrno,
|
||||
*/
|
||||
if (env->debugger) {
|
||||
env->xl = cpu_recompute_xl(env);
|
||||
riscv_cpu_update_mask(env);
|
||||
}
|
||||
|
||||
riscv_cpu_update_mask(env);
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
|
||||
@ -1351,7 +1346,7 @@ static RISCVException write_mstatush(CPURISCVState *env, int csrno,
|
||||
target_ulong val)
|
||||
{
|
||||
uint64_t valh = (uint64_t)val << 32;
|
||||
uint64_t mask = MSTATUS_MPV | MSTATUS_GVA;
|
||||
uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0;
|
||||
|
||||
env->mstatus = (env->mstatus & ~mask) | (valh & mask);
|
||||
|
||||
@ -3639,7 +3634,7 @@ static RISCVException write_mpmmask(CPURISCVState *env, int csrno,
|
||||
uint64_t mstatus;
|
||||
|
||||
env->mpmmask = val;
|
||||
if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) {
|
||||
if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) {
|
||||
env->cur_pmmask = val;
|
||||
}
|
||||
env->mmte |= EXT_STATUS_DIRTY;
|
||||
@ -3667,8 +3662,11 @@ static RISCVException write_spmmask(CPURISCVState *env, int csrno,
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
env->spmmask = val;
|
||||
if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) {
|
||||
if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) {
|
||||
env->cur_pmmask = val;
|
||||
if (cpu_get_xl(env, PRV_S) == MXL_RV32) {
|
||||
env->cur_pmmask &= UINT32_MAX;
|
||||
}
|
||||
}
|
||||
env->mmte |= EXT_STATUS_DIRTY;
|
||||
|
||||
@ -3695,8 +3693,11 @@ static RISCVException write_upmmask(CPURISCVState *env, int csrno,
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
env->upmmask = val;
|
||||
if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) {
|
||||
if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) {
|
||||
env->cur_pmmask = val;
|
||||
if (cpu_get_xl(env, PRV_U) == MXL_RV32) {
|
||||
env->cur_pmmask &= UINT32_MAX;
|
||||
}
|
||||
}
|
||||
env->mmte |= EXT_STATUS_DIRTY;
|
||||
|
||||
@ -3719,7 +3720,7 @@ static RISCVException write_mpmbase(CPURISCVState *env, int csrno,
|
||||
uint64_t mstatus;
|
||||
|
||||
env->mpmbase = val;
|
||||
if ((env->priv == PRV_M) && (env->mmte & M_PM_ENABLE)) {
|
||||
if ((cpu_address_mode(env) == PRV_M) && (env->mmte & M_PM_ENABLE)) {
|
||||
env->cur_pmbase = val;
|
||||
}
|
||||
env->mmte |= EXT_STATUS_DIRTY;
|
||||
@ -3747,8 +3748,11 @@ static RISCVException write_spmbase(CPURISCVState *env, int csrno,
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
env->spmbase = val;
|
||||
if ((env->priv == PRV_S) && (env->mmte & S_PM_ENABLE)) {
|
||||
if ((cpu_address_mode(env) == PRV_S) && (env->mmte & S_PM_ENABLE)) {
|
||||
env->cur_pmbase = val;
|
||||
if (cpu_get_xl(env, PRV_S) == MXL_RV32) {
|
||||
env->cur_pmbase &= UINT32_MAX;
|
||||
}
|
||||
}
|
||||
env->mmte |= EXT_STATUS_DIRTY;
|
||||
|
||||
@ -3775,8 +3779,11 @@ static RISCVException write_upmbase(CPURISCVState *env, int csrno,
|
||||
return RISCV_EXCP_NONE;
|
||||
}
|
||||
env->upmbase = val;
|
||||
if ((env->priv == PRV_U) && (env->mmte & U_PM_ENABLE)) {
|
||||
if ((cpu_address_mode(env) == PRV_U) && (env->mmte & U_PM_ENABLE)) {
|
||||
env->cur_pmbase = val;
|
||||
if (cpu_get_xl(env, PRV_U) == MXL_RV32) {
|
||||
env->cur_pmbase &= UINT32_MAX;
|
||||
}
|
||||
}
|
||||
env->mmte |= EXT_STATUS_DIRTY;
|
||||
|
||||
|
@ -252,6 +252,14 @@ uint64_t helper_fmin_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
float32_minimum_number(frs1, frs2, &env->fp_status));
|
||||
}
|
||||
|
||||
uint64_t helper_fminm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
float32 frs2 = check_nanbox_s(env, rs2);
|
||||
float32 ret = float32_min(frs1, frs2, &env->fp_status);
|
||||
return nanbox_s(env, ret);
|
||||
}
|
||||
|
||||
uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
@ -261,6 +269,14 @@ uint64_t helper_fmax_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
float32_maximum_number(frs1, frs2, &env->fp_status));
|
||||
}
|
||||
|
||||
uint64_t helper_fmaxm_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
float32 frs2 = check_nanbox_s(env, rs2);
|
||||
float32 ret = float32_max(frs1, frs2, &env->fp_status);
|
||||
return nanbox_s(env, ret);
|
||||
}
|
||||
|
||||
uint64_t helper_fsqrt_s(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
@ -274,6 +290,13 @@ target_ulong helper_fle_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
return float32_le(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_fleq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
float32 frs2 = check_nanbox_s(env, rs2);
|
||||
return float32_le_quiet(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
@ -281,6 +304,13 @@ target_ulong helper_flt_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
return float32_lt(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_fltq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
float32 frs2 = check_nanbox_s(env, rs2);
|
||||
return float32_lt_quiet(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_feq_s(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
@ -338,6 +368,30 @@ target_ulong helper_fclass_s(CPURISCVState *env, uint64_t rs1)
|
||||
return fclass_s(frs1);
|
||||
}
|
||||
|
||||
uint64_t helper_fround_s(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float_status *fs = &env->fp_status;
|
||||
uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact;
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
|
||||
frs1 = float32_round_to_int(frs1, fs);
|
||||
|
||||
/* Restore the original NX flag. */
|
||||
uint16_t flags = get_float_exception_flags(fs);
|
||||
flags &= ~float_flag_inexact;
|
||||
flags |= nx_old;
|
||||
set_float_exception_flags(flags, fs);
|
||||
|
||||
return nanbox_s(env, frs1);
|
||||
}
|
||||
|
||||
uint64_t helper_froundnx_s(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
frs1 = float32_round_to_int(frs1, &env->fp_status);
|
||||
return nanbox_s(env, frs1);
|
||||
}
|
||||
|
||||
uint64_t helper_fadd_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return float64_add(frs1, frs2, &env->fp_status);
|
||||
@ -365,6 +419,11 @@ uint64_t helper_fmin_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
float64_minimum_number(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
uint64_t helper_fminm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return float64_min(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return env->priv_ver < PRIV_VERSION_1_11_0 ?
|
||||
@ -372,6 +431,11 @@ uint64_t helper_fmax_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
float64_maximum_number(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
uint64_t helper_fmaxm_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return float64_max(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
uint64_t helper_fcvt_s_d(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
return nanbox_s(env, float64_to_float32(rs1, &env->fp_status));
|
||||
@ -393,11 +457,21 @@ target_ulong helper_fle_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
return float64_le(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_fleq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return float64_le_quiet(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_flt_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return float64_lt(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_fltq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return float64_lt_quiet(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_feq_d(CPURISCVState *env, uint64_t frs1, uint64_t frs2)
|
||||
{
|
||||
return float64_eq_quiet(frs1, frs2, &env->fp_status);
|
||||
@ -408,6 +482,11 @@ target_ulong helper_fcvt_w_d(CPURISCVState *env, uint64_t frs1)
|
||||
return float64_to_int32(frs1, &env->fp_status);
|
||||
}
|
||||
|
||||
uint64_t helper_fcvtmod_w_d(CPURISCVState *env, uint64_t value)
|
||||
{
|
||||
return float64_to_int32_modulo(value, float_round_to_zero, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_fcvt_wu_d(CPURISCVState *env, uint64_t frs1)
|
||||
{
|
||||
return (int32_t)float64_to_uint32(frs1, &env->fp_status);
|
||||
@ -448,6 +527,27 @@ target_ulong helper_fclass_d(uint64_t frs1)
|
||||
return fclass_d(frs1);
|
||||
}
|
||||
|
||||
uint64_t helper_fround_d(CPURISCVState *env, uint64_t frs1)
|
||||
{
|
||||
float_status *fs = &env->fp_status;
|
||||
uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact;
|
||||
|
||||
frs1 = float64_round_to_int(frs1, fs);
|
||||
|
||||
/* Restore the original NX flag. */
|
||||
uint16_t flags = get_float_exception_flags(fs);
|
||||
flags &= ~float_flag_inexact;
|
||||
flags |= nx_old;
|
||||
set_float_exception_flags(flags, fs);
|
||||
|
||||
return frs1;
|
||||
}
|
||||
|
||||
uint64_t helper_froundnx_d(CPURISCVState *env, uint64_t frs1)
|
||||
{
|
||||
return float64_round_to_int(frs1, &env->fp_status);
|
||||
}
|
||||
|
||||
uint64_t helper_fadd_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
@ -485,6 +585,14 @@ uint64_t helper_fmin_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
float16_minimum_number(frs1, frs2, &env->fp_status));
|
||||
}
|
||||
|
||||
uint64_t helper_fminm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
float16 frs2 = check_nanbox_h(env, rs2);
|
||||
float16 ret = float16_min(frs1, frs2, &env->fp_status);
|
||||
return nanbox_h(env, ret);
|
||||
}
|
||||
|
||||
uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
@ -494,6 +602,14 @@ uint64_t helper_fmax_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
float16_maximum_number(frs1, frs2, &env->fp_status));
|
||||
}
|
||||
|
||||
uint64_t helper_fmaxm_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
float16 frs2 = check_nanbox_h(env, rs2);
|
||||
float16 ret = float16_max(frs1, frs2, &env->fp_status);
|
||||
return nanbox_h(env, ret);
|
||||
}
|
||||
|
||||
uint64_t helper_fsqrt_h(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
@ -507,6 +623,13 @@ target_ulong helper_fle_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
return float16_le(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_fleq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
float16 frs2 = check_nanbox_h(env, rs2);
|
||||
return float16_le_quiet(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
@ -514,6 +637,13 @@ target_ulong helper_flt_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
return float16_lt(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_fltq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
float16 frs2 = check_nanbox_h(env, rs2);
|
||||
return float16_lt_quiet(frs1, frs2, &env->fp_status);
|
||||
}
|
||||
|
||||
target_ulong helper_feq_h(CPURISCVState *env, uint64_t rs1, uint64_t rs2)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
@ -527,6 +657,30 @@ target_ulong helper_fclass_h(CPURISCVState *env, uint64_t rs1)
|
||||
return fclass_h(frs1);
|
||||
}
|
||||
|
||||
uint64_t helper_fround_h(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float_status *fs = &env->fp_status;
|
||||
uint16_t nx_old = get_float_exception_flags(fs) & float_flag_inexact;
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
|
||||
frs1 = float16_round_to_int(frs1, fs);
|
||||
|
||||
/* Restore the original NX flag. */
|
||||
uint16_t flags = get_float_exception_flags(fs);
|
||||
flags &= ~float_flag_inexact;
|
||||
flags |= nx_old;
|
||||
set_float_exception_flags(flags, fs);
|
||||
|
||||
return nanbox_h(env, frs1);
|
||||
}
|
||||
|
||||
uint64_t helper_froundnx_h(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float16 frs1 = check_nanbox_s(env, rs1);
|
||||
frs1 = float16_round_to_int(frs1, &env->fp_status);
|
||||
return nanbox_h(env, frs1);
|
||||
}
|
||||
|
||||
target_ulong helper_fcvt_w_h(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
@ -593,3 +747,15 @@ uint64_t helper_fcvt_d_h(CPURISCVState *env, uint64_t rs1)
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
return float16_to_float64(frs1, true, &env->fp_status);
|
||||
}
|
||||
|
||||
uint64_t helper_fcvt_bf16_s(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float32 frs1 = check_nanbox_s(env, rs1);
|
||||
return nanbox_h(env, float32_to_bfloat16(frs1, &env->fp_status));
|
||||
}
|
||||
|
||||
uint64_t helper_fcvt_s_bf16(CPURISCVState *env, uint64_t rs1)
|
||||
{
|
||||
float16 frs1 = check_nanbox_h(env, rs1);
|
||||
return nanbox_s(env, bfloat16_to_float32(frs1, &env->fp_status));
|
||||
}
|
||||
|
@ -25,10 +25,14 @@ DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fminm_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmaxm_s, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_3(fle_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fleq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(flt_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fltq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(feq_s, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_w_s, TCG_CALL_NO_RWG, tl, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_wu_s, TCG_CALL_NO_RWG, tl, env, i64)
|
||||
@ -39,6 +43,8 @@ DEF_HELPER_FLAGS_2(fcvt_s_wu, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fcvt_s_l, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fcvt_s_lu, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, tl, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fround_s, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(froundnx_s, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
|
||||
/* Floating Point - Double Precision */
|
||||
DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
@ -46,14 +52,19 @@ DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fminm_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmaxm_d, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_s_d, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_d_s, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_3(fle_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fleq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(flt_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fltq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(feq_d, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_w_d, TCG_CALL_NO_RWG, tl, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvtmod_w_d, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_wu_d, TCG_CALL_NO_RWG, tl, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_l_d, TCG_CALL_NO_RWG, tl, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_lu_d, TCG_CALL_NO_RWG, tl, env, i64)
|
||||
@ -62,6 +73,8 @@ DEF_HELPER_FLAGS_2(fcvt_d_wu, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fcvt_d_l, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fcvt_d_lu, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_1(fclass_d, TCG_CALL_NO_RWG_SE, tl, i64)
|
||||
DEF_HELPER_FLAGS_2(fround_d, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(froundnx_d, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
|
||||
/* Bitmanip */
|
||||
DEF_HELPER_FLAGS_2(clmul, TCG_CALL_NO_RWG_SE, tl, tl, tl)
|
||||
@ -78,10 +91,14 @@ DEF_HELPER_FLAGS_3(fsub_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmul_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fdiv_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmin_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fminm_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmax_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fmaxm_h, TCG_CALL_NO_RWG, i64, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(fsqrt_h, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_3(fle_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fleq_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(flt_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(fltq_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_3(feq_h, TCG_CALL_NO_RWG, tl, env, i64, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_s_h, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_h_s, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
@ -96,6 +113,8 @@ DEF_HELPER_FLAGS_2(fcvt_h_wu, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fcvt_h_l, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fcvt_h_lu, TCG_CALL_NO_RWG, i64, env, tl)
|
||||
DEF_HELPER_FLAGS_2(fclass_h, TCG_CALL_NO_RWG_SE, tl, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fround_h, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(froundnx_h, TCG_CALL_NO_RWG_SE, i64, env, i64)
|
||||
|
||||
/* Cache-block operations */
|
||||
DEF_HELPER_2(cbo_clean_flush, void, env, tl)
|
||||
@ -1153,3 +1172,13 @@ DEF_HELPER_FLAGS_3(sm4ks, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl)
|
||||
|
||||
/* Zce helper */
|
||||
DEF_HELPER_FLAGS_2(cm_jalt, TCG_CALL_NO_WG, tl, env, i32)
|
||||
|
||||
/* BF16 functions */
|
||||
DEF_HELPER_FLAGS_2(fcvt_bf16_s, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
DEF_HELPER_FLAGS_2(fcvt_s_bf16, TCG_CALL_NO_RWG, i64, env, i64)
|
||||
|
||||
DEF_HELPER_5(vfncvtbf16_f_f_w, void, ptr, ptr, ptr, env, i32)
|
||||
DEF_HELPER_5(vfwcvtbf16_f_f_v, void, ptr, ptr, ptr, env, i32)
|
||||
|
||||
DEF_HELPER_6(vfwmaccbf16_vv, void, ptr, ptr, ptr, ptr, env, i32)
|
||||
DEF_HELPER_6(vfwmaccbf16_vf, void, ptr, ptr, i64, ptr, env, i32)
|
||||
|
@ -821,6 +821,32 @@ binvi 01101. ........... 001 ..... 0010011 @sh
|
||||
bset 0010100 .......... 001 ..... 0110011 @r
|
||||
bseti 00101. ........... 001 ..... 0010011 @sh
|
||||
|
||||
# *** Zfa Standard Extension ***
|
||||
fli_s 1111000 00001 ..... 000 ..... 1010011 @r2
|
||||
fli_d 1111001 00001 ..... 000 ..... 1010011 @r2
|
||||
fli_h 1111010 00001 ..... 000 ..... 1010011 @r2
|
||||
fminm_s 0010100 ..... ..... 010 ..... 1010011 @r
|
||||
fmaxm_s 0010100 ..... ..... 011 ..... 1010011 @r
|
||||
fminm_d 0010101 ..... ..... 010 ..... 1010011 @r
|
||||
fmaxm_d 0010101 ..... ..... 011 ..... 1010011 @r
|
||||
fminm_h 0010110 ..... ..... 010 ..... 1010011 @r
|
||||
fmaxm_h 0010110 ..... ..... 011 ..... 1010011 @r
|
||||
fround_s 0100000 00100 ..... ... ..... 1010011 @r2_rm
|
||||
froundnx_s 0100000 00101 ..... ... ..... 1010011 @r2_rm
|
||||
fround_d 0100001 00100 ..... ... ..... 1010011 @r2_rm
|
||||
froundnx_d 0100001 00101 ..... ... ..... 1010011 @r2_rm
|
||||
fround_h 0100010 00100 ..... ... ..... 1010011 @r2_rm
|
||||
froundnx_h 0100010 00101 ..... ... ..... 1010011 @r2_rm
|
||||
fcvtmod_w_d 1100001 01000 ..... 001 ..... 1010011 @r2
|
||||
fmvh_x_d 1110001 00001 ..... 000 ..... 1010011 @r2
|
||||
fmvp_d_x 1011001 ..... ..... 000 ..... 1010011 @r
|
||||
fleq_s 1010000 ..... ..... 100 ..... 1010011 @r
|
||||
fltq_s 1010000 ..... ..... 101 ..... 1010011 @r
|
||||
fleq_d 1010001 ..... ..... 100 ..... 1010011 @r
|
||||
fltq_d 1010001 ..... ..... 101 ..... 1010011 @r
|
||||
fleq_h 1010010 ..... ..... 100 ..... 1010011 @r
|
||||
fltq_h 1010010 ..... ..... 101 ..... 1010011 @r
|
||||
|
||||
# *** RV32 Zfh Extension ***
|
||||
flh ............ ..... 001 ..... 0000111 @i
|
||||
fsh ....... ..... ..... 001 ..... 0100111 @s
|
||||
@ -908,3 +934,15 @@ sm4ks .. 11010 ..... ..... 000 ..... 0110011 @k_aes
|
||||
# *** RV32 Zicond Standard Extension ***
|
||||
czero_eqz 0000111 ..... ..... 101 ..... 0110011 @r
|
||||
czero_nez 0000111 ..... ..... 111 ..... 0110011 @r
|
||||
|
||||
# *** Zfbfmin Standard Extension ***
|
||||
fcvt_bf16_s 0100010 01000 ..... ... ..... 1010011 @r2_rm
|
||||
fcvt_s_bf16 0100000 00110 ..... ... ..... 1010011 @r2_rm
|
||||
|
||||
# *** Zvfbfmin Standard Extension ***
|
||||
vfncvtbf16_f_f_w 010010 . ..... 11101 001 ..... 1010111 @r2_vm
|
||||
vfwcvtbf16_f_f_v 010010 . ..... 01101 001 ..... 1010111 @r2_vm
|
||||
|
||||
# *** Zvfbfwma Standard Extension ***
|
||||
vfwmaccbf16_vv 111011 . ..... ..... 001 ..... 1010111 @r_vm
|
||||
vfwmaccbf16_vf 111011 . ..... ..... 101 ..... 1010111 @r_vm
|
||||
|
175
target/riscv/insn_trans/trans_rvbf16.c.inc
Normal file
175
target/riscv/insn_trans/trans_rvbf16.c.inc
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* RISC-V translation routines for the BF16 Standard Extensions.
|
||||
*
|
||||
* Copyright (c) 2020-2023 PLCT Lab
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define REQUIRE_ZFBFMIN(ctx) do { \
|
||||
if (!ctx->cfg_ptr->ext_zfbfmin) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_ZVFBFMIN(ctx) do { \
|
||||
if (!ctx->cfg_ptr->ext_zvfbfmin) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_ZVFBFWMA(ctx) do { \
|
||||
if (!ctx->cfg_ptr->ext_zvfbfwma) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool trans_fcvt_bf16_s(DisasContext *ctx, arg_fcvt_bf16_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFBFMIN(ctx);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_fcvt_bf16_s(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fcvt_s_bf16(DisasContext *ctx, arg_fcvt_s_bf16 *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFBFMIN(ctx);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_fcvt_s_bf16(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_vfncvtbf16_f_f_w(DisasContext *ctx, arg_vfncvtbf16_f_f_w *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZVFBFMIN(ctx);
|
||||
|
||||
if (opfv_narrow_check(ctx, a) && (ctx->sew == MO_16)) {
|
||||
uint32_t data = 0;
|
||||
TCGLabel *over = gen_new_label();
|
||||
|
||||
gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN);
|
||||
tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over);
|
||||
tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
|
||||
|
||||
data = FIELD_DP32(data, VDATA, VM, a->vm);
|
||||
data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
|
||||
data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
|
||||
data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
|
||||
tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0),
|
||||
vreg_ofs(ctx, a->rs2), cpu_env,
|
||||
ctx->cfg_ptr->vlen / 8,
|
||||
ctx->cfg_ptr->vlen / 8, data,
|
||||
gen_helper_vfncvtbf16_f_f_w);
|
||||
mark_vs_dirty(ctx);
|
||||
gen_set_label(over);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool trans_vfwcvtbf16_f_f_v(DisasContext *ctx, arg_vfwcvtbf16_f_f_v *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZVFBFMIN(ctx);
|
||||
|
||||
if (opfv_widen_check(ctx, a) && (ctx->sew == MO_16)) {
|
||||
uint32_t data = 0;
|
||||
TCGLabel *over = gen_new_label();
|
||||
|
||||
gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN);
|
||||
tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over);
|
||||
tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
|
||||
|
||||
data = FIELD_DP32(data, VDATA, VM, a->vm);
|
||||
data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
|
||||
data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
|
||||
data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
|
||||
tcg_gen_gvec_3_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0),
|
||||
vreg_ofs(ctx, a->rs2), cpu_env,
|
||||
ctx->cfg_ptr->vlen / 8,
|
||||
ctx->cfg_ptr->vlen / 8, data,
|
||||
gen_helper_vfwcvtbf16_f_f_v);
|
||||
mark_vs_dirty(ctx);
|
||||
gen_set_label(over);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool trans_vfwmaccbf16_vv(DisasContext *ctx, arg_vfwmaccbf16_vv *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZVFBFWMA(ctx);
|
||||
|
||||
if (require_rvv(ctx) && vext_check_isa_ill(ctx) && (ctx->sew == MO_16) &&
|
||||
vext_check_dss(ctx, a->rd, a->rs1, a->rs2, a->vm)) {
|
||||
uint32_t data = 0;
|
||||
TCGLabel *over = gen_new_label();
|
||||
|
||||
gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN);
|
||||
tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_vl, 0, over);
|
||||
tcg_gen_brcond_tl(TCG_COND_GEU, cpu_vstart, cpu_vl, over);
|
||||
|
||||
data = FIELD_DP32(data, VDATA, VM, a->vm);
|
||||
data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
|
||||
data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
|
||||
data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
|
||||
tcg_gen_gvec_4_ptr(vreg_ofs(ctx, a->rd), vreg_ofs(ctx, 0),
|
||||
vreg_ofs(ctx, a->rs1),
|
||||
vreg_ofs(ctx, a->rs2), cpu_env,
|
||||
ctx->cfg_ptr->vlen / 8,
|
||||
ctx->cfg_ptr->vlen / 8, data,
|
||||
gen_helper_vfwmaccbf16_vv);
|
||||
mark_vs_dirty(ctx);
|
||||
gen_set_label(over);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool trans_vfwmaccbf16_vf(DisasContext *ctx, arg_vfwmaccbf16_vf *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZVFBFWMA(ctx);
|
||||
|
||||
if (require_rvv(ctx) && (ctx->sew == MO_16) && vext_check_isa_ill(ctx) &&
|
||||
vext_check_ds(ctx, a->rd, a->rs2, a->vm)) {
|
||||
uint32_t data = 0;
|
||||
|
||||
gen_set_rm(ctx, RISCV_FRM_DYN);
|
||||
data = FIELD_DP32(data, VDATA, VM, a->vm);
|
||||
data = FIELD_DP32(data, VDATA, LMUL, ctx->lmul);
|
||||
data = FIELD_DP32(data, VDATA, VTA, ctx->vta);
|
||||
data = FIELD_DP32(data, VDATA, VMA, ctx->vma);
|
||||
return opfvf_trans(a->rd, a->rs1, a->rs2, data,
|
||||
gen_helper_vfwmaccbf16_vf, ctx);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
521
target/riscv/insn_trans/trans_rvzfa.c.inc
Normal file
521
target/riscv/insn_trans/trans_rvzfa.c.inc
Normal file
@ -0,0 +1,521 @@
|
||||
/*
|
||||
* RISC-V translation routines for the Zfa Standard Extension.
|
||||
*
|
||||
* Copyright (c) 2023 Christoph Müllner, christoph.muellner@vrull.eu
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define REQUIRE_ZFA(ctx) do { \
|
||||
if (!ctx->cfg_ptr->ext_zfa) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_ZFH(ctx) do { \
|
||||
if (!ctx->cfg_ptr->ext_zfh) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static bool trans_fli_s(DisasContext *ctx, arg_fli_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVF);
|
||||
|
||||
/* Values below are NaN-boxed to avoid a gen_nanbox_s(). */
|
||||
static const uint64_t fli_s_table[] = {
|
||||
0xffffffffbf800000, /* -1.0 */
|
||||
0xffffffff00800000, /* minimum positive normal */
|
||||
0xffffffff37800000, /* 1.0 * 2^-16 */
|
||||
0xffffffff38000000, /* 1.0 * 2^-15 */
|
||||
0xffffffff3b800000, /* 1.0 * 2^-8 */
|
||||
0xffffffff3c000000, /* 1.0 * 2^-7 */
|
||||
0xffffffff3d800000, /* 1.0 * 2^-4 */
|
||||
0xffffffff3e000000, /* 1.0 * 2^-3 */
|
||||
0xffffffff3e800000, /* 0.25 */
|
||||
0xffffffff3ea00000, /* 0.3125 */
|
||||
0xffffffff3ec00000, /* 0.375 */
|
||||
0xffffffff3ee00000, /* 0.4375 */
|
||||
0xffffffff3f000000, /* 0.5 */
|
||||
0xffffffff3f200000, /* 0.625 */
|
||||
0xffffffff3f400000, /* 0.75 */
|
||||
0xffffffff3f600000, /* 0.875 */
|
||||
0xffffffff3f800000, /* 1.0 */
|
||||
0xffffffff3fa00000, /* 1.25 */
|
||||
0xffffffff3fc00000, /* 1.5 */
|
||||
0xffffffff3fe00000, /* 1.75 */
|
||||
0xffffffff40000000, /* 2.0 */
|
||||
0xffffffff40200000, /* 2.5 */
|
||||
0xffffffff40400000, /* 3 */
|
||||
0xffffffff40800000, /* 4 */
|
||||
0xffffffff41000000, /* 8 */
|
||||
0xffffffff41800000, /* 16 */
|
||||
0xffffffff43000000, /* 2^7 */
|
||||
0xffffffff43800000, /* 2^8 */
|
||||
0xffffffff47000000, /* 2^15 */
|
||||
0xffffffff47800000, /* 2^16 */
|
||||
0xffffffff7f800000, /* +inf */
|
||||
0xffffffff7fc00000, /* Canonical NaN */
|
||||
};
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
tcg_gen_movi_i64(dest, fli_s_table[a->rs1]);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fli_d(DisasContext *ctx, arg_fli_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
static const uint64_t fli_d_table[] = {
|
||||
0xbff0000000000000, /* -1.0 */
|
||||
0x0010000000000000, /* minimum positive normal */
|
||||
0x3ef0000000000000, /* 1.0 * 2^-16 */
|
||||
0x3f00000000000000, /* 1.0 * 2^-15 */
|
||||
0x3f70000000000000, /* 1.0 * 2^-8 */
|
||||
0x3f80000000000000, /* 1.0 * 2^-7 */
|
||||
0x3fb0000000000000, /* 1.0 * 2^-4 */
|
||||
0x3fc0000000000000, /* 1.0 * 2^-3 */
|
||||
0x3fd0000000000000, /* 0.25 */
|
||||
0x3fd4000000000000, /* 0.3125 */
|
||||
0x3fd8000000000000, /* 0.375 */
|
||||
0x3fdc000000000000, /* 0.4375 */
|
||||
0x3fe0000000000000, /* 0.5 */
|
||||
0x3fe4000000000000, /* 0.625 */
|
||||
0x3fe8000000000000, /* 0.75 */
|
||||
0x3fec000000000000, /* 0.875 */
|
||||
0x3ff0000000000000, /* 1.0 */
|
||||
0x3ff4000000000000, /* 1.25 */
|
||||
0x3ff8000000000000, /* 1.5 */
|
||||
0x3ffc000000000000, /* 1.75 */
|
||||
0x4000000000000000, /* 2.0 */
|
||||
0x4004000000000000, /* 2.5 */
|
||||
0x4008000000000000, /* 3 */
|
||||
0x4010000000000000, /* 4 */
|
||||
0x4020000000000000, /* 8 */
|
||||
0x4030000000000000, /* 16 */
|
||||
0x4060000000000000, /* 2^7 */
|
||||
0x4070000000000000, /* 2^8 */
|
||||
0x40e0000000000000, /* 2^15 */
|
||||
0x40f0000000000000, /* 2^16 */
|
||||
0x7ff0000000000000, /* +inf */
|
||||
0x7ff8000000000000, /* Canonical NaN */
|
||||
};
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
tcg_gen_movi_i64(dest, fli_d_table[a->rs1]);
|
||||
gen_set_fpr_d(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fli_h(DisasContext *ctx, arg_fli_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_ZFH(ctx);
|
||||
|
||||
/* Values below are NaN-boxed to avoid a gen_nanbox_h(). */
|
||||
static const uint64_t fli_h_table[] = {
|
||||
0xffffffffffffbc00, /* -1.0 */
|
||||
0xffffffffffff0400, /* minimum positive normal */
|
||||
0xffffffffffff0100, /* 1.0 * 2^-16 */
|
||||
0xffffffffffff0200, /* 1.0 * 2^-15 */
|
||||
0xffffffffffff1c00, /* 1.0 * 2^-8 */
|
||||
0xffffffffffff2000, /* 1.0 * 2^-7 */
|
||||
0xffffffffffff2c00, /* 1.0 * 2^-4 */
|
||||
0xffffffffffff3000, /* 1.0 * 2^-3 */
|
||||
0xffffffffffff3400, /* 0.25 */
|
||||
0xffffffffffff3500, /* 0.3125 */
|
||||
0xffffffffffff3600, /* 0.375 */
|
||||
0xffffffffffff3700, /* 0.4375 */
|
||||
0xffffffffffff3800, /* 0.5 */
|
||||
0xffffffffffff3900, /* 0.625 */
|
||||
0xffffffffffff3a00, /* 0.75 */
|
||||
0xffffffffffff3b00, /* 0.875 */
|
||||
0xffffffffffff3c00, /* 1.0 */
|
||||
0xffffffffffff3d00, /* 1.25 */
|
||||
0xffffffffffff3e00, /* 1.5 */
|
||||
0xffffffffffff3f00, /* 1.75 */
|
||||
0xffffffffffff4000, /* 2.0 */
|
||||
0xffffffffffff4100, /* 2.5 */
|
||||
0xffffffffffff4200, /* 3 */
|
||||
0xffffffffffff4400, /* 4 */
|
||||
0xffffffffffff4800, /* 8 */
|
||||
0xffffffffffff4c00, /* 16 */
|
||||
0xffffffffffff5800, /* 2^7 */
|
||||
0xffffffffffff5c00, /* 2^8 */
|
||||
0xffffffffffff7800, /* 2^15 */
|
||||
0xffffffffffff7c00, /* 2^16 */
|
||||
0xffffffffffff7c00, /* +inf */
|
||||
0xffffffffffff7e00, /* Canonical NaN */
|
||||
};
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
tcg_gen_movi_i64(dest, fli_h_table[a->rs1]);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fminm_s(DisasContext *ctx, arg_fminm_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVF);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fminm_s(dest, cpu_env, src1, src2);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fmaxm_s(DisasContext *ctx, arg_fmaxm_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVF);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fmaxm_s(dest, cpu_env, src1, src2);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fminm_d(DisasContext *ctx, arg_fminm_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
|
||||
|
||||
gen_helper_fminm_d(dest, cpu_env, src1, src2);
|
||||
gen_set_fpr_d(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fmaxm_d(DisasContext *ctx, arg_fmaxm_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_d(ctx, a->rs2);
|
||||
|
||||
gen_helper_fmaxm_d(dest, cpu_env, src1, src2);
|
||||
gen_set_fpr_d(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fminm_h(DisasContext *ctx, arg_fminm_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_ZFH(ctx);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fminm_h(dest, cpu_env, src1, src2);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fmaxm_h(DisasContext *ctx, arg_fmaxm_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_ZFH(ctx);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fmaxm_h(dest, cpu_env, src1, src2);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fround_s(DisasContext *ctx, arg_fround_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVF);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_fround_s(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_froundnx_s(DisasContext *ctx, arg_froundnx_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVF);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_froundnx_s(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fround_d(DisasContext *ctx, arg_fround_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_fround_d(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_froundnx_d(DisasContext *ctx, arg_froundnx_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_froundnx_d(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_fround_h(DisasContext *ctx, arg_fround_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_ZFH(ctx);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_fround_h(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_froundnx_h(DisasContext *ctx, arg_froundnx_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_ZFH(ctx);
|
||||
|
||||
TCGv_i64 dest = dest_fpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
|
||||
gen_set_rm(ctx, a->rm);
|
||||
gen_helper_froundnx_h(dest, cpu_env, src1);
|
||||
gen_set_fpr_hs(ctx, a->rd, dest);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fcvtmod_w_d(DisasContext *ctx, arg_fcvtmod_w_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
TCGv dst = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_d(ctx, a->rs1);
|
||||
TCGv_i64 t1 = tcg_temp_new_i64();
|
||||
|
||||
/* Rounding mode is RTZ. */
|
||||
gen_set_rm(ctx, RISCV_FRM_RTZ);
|
||||
gen_helper_fcvtmod_w_d(t1, cpu_env, src1);
|
||||
tcg_gen_trunc_i64_tl(dst, t1);
|
||||
gen_set_gpr(ctx, a->rd, dst);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fmvh_x_d(DisasContext *ctx, arg_fmvh_x_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
REQUIRE_32BIT(ctx);
|
||||
|
||||
TCGv dst = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 t1 = tcg_temp_new_i64();
|
||||
tcg_gen_sari_i64(t1, cpu_fpr[a->rs1], 32);
|
||||
tcg_gen_trunc_i64_tl(dst, t1);
|
||||
gen_set_gpr(ctx, a->rd, dst);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fmvp_d_x(DisasContext *ctx, arg_fmvp_d_x *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
REQUIRE_32BIT(ctx);
|
||||
|
||||
TCGv src1 = get_gpr(ctx, a->rs1, EXT_NONE);
|
||||
TCGv src2 = get_gpr(ctx, a->rs2, EXT_NONE);
|
||||
tcg_gen_concat_tl_i64(cpu_fpr[a->rd], src1, src2);
|
||||
|
||||
mark_fs_dirty(ctx);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fleq_s(DisasContext *ctx, arg_fleq_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVF);
|
||||
|
||||
TCGv dest = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fleq_s(dest, cpu_env, src1, src2);
|
||||
gen_set_gpr(ctx, a->rd, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fltq_s(DisasContext *ctx, arg_fltq_s *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVF);
|
||||
|
||||
TCGv dest = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fltq_s(dest, cpu_env, src1, src2);
|
||||
gen_set_gpr(ctx, a->rd, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fleq_d(DisasContext *ctx, arg_fleq_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
TCGv dest = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fltq_s(dest, cpu_env, src1, src2);
|
||||
gen_set_gpr(ctx, a->rd, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fltq_d(DisasContext *ctx, arg_fltq_d *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_EXT(ctx, RVD);
|
||||
|
||||
TCGv dest = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fltq_s(dest, cpu_env, src1, src2);
|
||||
gen_set_gpr(ctx, a->rd, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fleq_h(DisasContext *ctx, arg_fleq_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_ZFH(ctx);
|
||||
|
||||
TCGv dest = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fleq_h(dest, cpu_env, src1, src2);
|
||||
gen_set_gpr(ctx, a->rd, dest);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool trans_fltq_h(DisasContext *ctx, arg_fltq_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFA(ctx);
|
||||
REQUIRE_ZFH(ctx);
|
||||
|
||||
TCGv dest = dest_gpr(ctx, a->rd);
|
||||
TCGv_i64 src1 = get_fpr_hs(ctx, a->rs1);
|
||||
TCGv_i64 src2 = get_fpr_hs(ctx, a->rs2);
|
||||
|
||||
gen_helper_fltq_h(dest, cpu_env, src1, src2);
|
||||
gen_set_gpr(ctx, a->rd, dest);
|
||||
return true;
|
||||
}
|
@ -28,8 +28,8 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define REQUIRE_ZFHMIN(ctx) do { \
|
||||
if (!ctx->cfg_ptr->ext_zfhmin) { \
|
||||
#define REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx) do { \
|
||||
if (!ctx->cfg_ptr->ext_zfhmin && !ctx->cfg_ptr->ext_zfbfmin) { \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
@ -46,7 +46,7 @@ static bool trans_flh(DisasContext *ctx, arg_flh *a)
|
||||
TCGv t0;
|
||||
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFHMIN(ctx);
|
||||
REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
|
||||
|
||||
decode_save_opc(ctx);
|
||||
t0 = get_gpr(ctx, a->rs1, EXT_NONE);
|
||||
@ -69,7 +69,7 @@ static bool trans_fsh(DisasContext *ctx, arg_fsh *a)
|
||||
TCGv t0;
|
||||
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFHMIN(ctx);
|
||||
REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
|
||||
|
||||
decode_save_opc(ctx);
|
||||
t0 = get_gpr(ctx, a->rs1, EXT_NONE);
|
||||
@ -574,7 +574,7 @@ static bool trans_fcvt_h_wu(DisasContext *ctx, arg_fcvt_h_wu *a)
|
||||
static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFHMIN(ctx);
|
||||
REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
|
||||
|
||||
TCGv dest = dest_gpr(ctx, a->rd);
|
||||
|
||||
@ -594,7 +594,7 @@ static bool trans_fmv_x_h(DisasContext *ctx, arg_fmv_x_h *a)
|
||||
static bool trans_fmv_h_x(DisasContext *ctx, arg_fmv_h_x *a)
|
||||
{
|
||||
REQUIRE_FPU;
|
||||
REQUIRE_ZFHMIN(ctx);
|
||||
REQUIRE_ZFHMIN_OR_ZFBFMIN(ctx);
|
||||
|
||||
TCGv t0 = get_gpr(ctx, a->rs1, EXT_ZERO);
|
||||
|
||||
|
@ -22,8 +22,10 @@
|
||||
#include <linux/kvm.h>
|
||||
|
||||
#include "qemu/timer.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qapi/visitor.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/kvm_int.h"
|
||||
@ -99,12 +101,280 @@ static uint64_t kvm_riscv_reg_id(CPURISCVState *env, uint64_t type,
|
||||
|
||||
#define KVM_RISCV_SET_TIMER(cs, env, name, reg) \
|
||||
do { \
|
||||
int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, time), ®); \
|
||||
int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, name), ®); \
|
||||
if (ret) { \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
typedef struct KVMCPUConfig {
|
||||
const char *name;
|
||||
const char *description;
|
||||
target_ulong offset;
|
||||
int kvm_reg_id;
|
||||
bool user_set;
|
||||
bool supported;
|
||||
} KVMCPUConfig;
|
||||
|
||||
#define KVM_MISA_CFG(_bit, _reg_id) \
|
||||
{.offset = _bit, .kvm_reg_id = _reg_id}
|
||||
|
||||
/* KVM ISA extensions */
|
||||
static KVMCPUConfig kvm_misa_ext_cfgs[] = {
|
||||
KVM_MISA_CFG(RVA, KVM_RISCV_ISA_EXT_A),
|
||||
KVM_MISA_CFG(RVC, KVM_RISCV_ISA_EXT_C),
|
||||
KVM_MISA_CFG(RVD, KVM_RISCV_ISA_EXT_D),
|
||||
KVM_MISA_CFG(RVF, KVM_RISCV_ISA_EXT_F),
|
||||
KVM_MISA_CFG(RVH, KVM_RISCV_ISA_EXT_H),
|
||||
KVM_MISA_CFG(RVI, KVM_RISCV_ISA_EXT_I),
|
||||
KVM_MISA_CFG(RVM, KVM_RISCV_ISA_EXT_M),
|
||||
};
|
||||
|
||||
static void kvm_cpu_set_misa_ext_cfg(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
KVMCPUConfig *misa_ext_cfg = opaque;
|
||||
target_ulong misa_bit = misa_ext_cfg->offset;
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
bool value, host_bit;
|
||||
|
||||
if (!visit_type_bool(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
host_bit = env->misa_ext_mask & misa_bit;
|
||||
|
||||
if (value == host_bit) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
misa_ext_cfg->user_set = true;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Forbid users to enable extensions that aren't
|
||||
* available in the hart.
|
||||
*/
|
||||
error_setg(errp, "Enabling MISA bit '%s' is not allowed: it's not "
|
||||
"enabled in the host", misa_ext_cfg->name);
|
||||
}
|
||||
|
||||
static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
uint64_t id, reg;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) {
|
||||
KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i];
|
||||
target_ulong misa_bit = misa_cfg->offset;
|
||||
|
||||
if (!misa_cfg->user_set) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* If we're here we're going to disable the MISA bit */
|
||||
reg = 0;
|
||||
id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT,
|
||||
misa_cfg->kvm_reg_id);
|
||||
ret = kvm_set_one_reg(cs, id, ®);
|
||||
if (ret != 0) {
|
||||
/*
|
||||
* We're not checking for -EINVAL because if the bit is about
|
||||
* to be disabled, it means that it was already enabled by
|
||||
* KVM. We determined that by fetching the 'isa' register
|
||||
* during init() time. Any error at this point is worth
|
||||
* aborting.
|
||||
*/
|
||||
error_report("Unable to set KVM reg %s, error %d",
|
||||
misa_cfg->name, ret);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
env->misa_ext &= ~misa_bit;
|
||||
}
|
||||
}
|
||||
|
||||
#define CPUCFG(_prop) offsetof(struct RISCVCPUConfig, _prop)
|
||||
|
||||
#define KVM_EXT_CFG(_name, _prop, _reg_id) \
|
||||
{.name = _name, .offset = CPUCFG(_prop), \
|
||||
.kvm_reg_id = _reg_id}
|
||||
|
||||
static KVMCPUConfig kvm_multi_ext_cfgs[] = {
|
||||
KVM_EXT_CFG("zicbom", ext_icbom, KVM_RISCV_ISA_EXT_ZICBOM),
|
||||
KVM_EXT_CFG("zicboz", ext_icboz, KVM_RISCV_ISA_EXT_ZICBOZ),
|
||||
KVM_EXT_CFG("zihintpause", ext_zihintpause, KVM_RISCV_ISA_EXT_ZIHINTPAUSE),
|
||||
KVM_EXT_CFG("zbb", ext_zbb, KVM_RISCV_ISA_EXT_ZBB),
|
||||
KVM_EXT_CFG("ssaia", ext_ssaia, KVM_RISCV_ISA_EXT_SSAIA),
|
||||
KVM_EXT_CFG("sstc", ext_sstc, KVM_RISCV_ISA_EXT_SSTC),
|
||||
KVM_EXT_CFG("svinval", ext_svinval, KVM_RISCV_ISA_EXT_SVINVAL),
|
||||
KVM_EXT_CFG("svpbmt", ext_svpbmt, KVM_RISCV_ISA_EXT_SVPBMT),
|
||||
};
|
||||
|
||||
static void *kvmconfig_get_cfg_addr(RISCVCPU *cpu, KVMCPUConfig *kvmcfg)
|
||||
{
|
||||
return (void *)&cpu->cfg + kvmcfg->offset;
|
||||
}
|
||||
|
||||
static void kvm_cpu_cfg_set(RISCVCPU *cpu, KVMCPUConfig *multi_ext,
|
||||
uint32_t val)
|
||||
{
|
||||
bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext);
|
||||
|
||||
*ext_enabled = val;
|
||||
}
|
||||
|
||||
static uint32_t kvm_cpu_cfg_get(RISCVCPU *cpu,
|
||||
KVMCPUConfig *multi_ext)
|
||||
{
|
||||
bool *ext_enabled = kvmconfig_get_cfg_addr(cpu, multi_ext);
|
||||
|
||||
return *ext_enabled;
|
||||
}
|
||||
|
||||
static void kvm_cpu_set_multi_ext_cfg(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
KVMCPUConfig *multi_ext_cfg = opaque;
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
bool value, host_val;
|
||||
|
||||
if (!visit_type_bool(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
host_val = kvm_cpu_cfg_get(cpu, multi_ext_cfg);
|
||||
|
||||
/*
|
||||
* Ignore if the user is setting the same value
|
||||
* as the host.
|
||||
*/
|
||||
if (value == host_val) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!multi_ext_cfg->supported) {
|
||||
/*
|
||||
* Error out if the user is trying to enable an
|
||||
* extension that KVM doesn't support. Ignore
|
||||
* option otherwise.
|
||||
*/
|
||||
if (value) {
|
||||
error_setg(errp, "KVM does not support disabling extension %s",
|
||||
multi_ext_cfg->name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
multi_ext_cfg->user_set = true;
|
||||
kvm_cpu_cfg_set(cpu, multi_ext_cfg, value);
|
||||
}
|
||||
|
||||
static KVMCPUConfig kvm_cbom_blocksize = {
|
||||
.name = "cbom_blocksize",
|
||||
.offset = CPUCFG(cbom_blocksize),
|
||||
.kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicbom_block_size)
|
||||
};
|
||||
|
||||
static KVMCPUConfig kvm_cboz_blocksize = {
|
||||
.name = "cboz_blocksize",
|
||||
.offset = CPUCFG(cboz_blocksize),
|
||||
.kvm_reg_id = KVM_REG_RISCV_CONFIG_REG(zicboz_block_size)
|
||||
};
|
||||
|
||||
static void kvm_cpu_set_cbomz_blksize(Object *obj, Visitor *v,
|
||||
const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
KVMCPUConfig *cbomz_cfg = opaque;
|
||||
RISCVCPU *cpu = RISCV_CPU(obj);
|
||||
uint16_t value, *host_val;
|
||||
|
||||
if (!visit_type_uint16(v, name, &value, errp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
host_val = kvmconfig_get_cfg_addr(cpu, cbomz_cfg);
|
||||
|
||||
if (value != *host_val) {
|
||||
error_report("Unable to set %s to a different value than "
|
||||
"the host (%u)",
|
||||
cbomz_cfg->name, *host_val);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
cbomz_cfg->user_set = true;
|
||||
}
|
||||
|
||||
static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
uint64_t id, reg;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
|
||||
KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i];
|
||||
|
||||
if (!multi_ext_cfg->user_set) {
|
||||
continue;
|
||||
}
|
||||
|
||||
id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT,
|
||||
multi_ext_cfg->kvm_reg_id);
|
||||
reg = kvm_cpu_cfg_get(cpu, multi_ext_cfg);
|
||||
ret = kvm_set_one_reg(cs, id, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to %s extension %s in KVM, error %d",
|
||||
reg ? "enable" : "disable",
|
||||
multi_ext_cfg->name, ret);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_riscv_add_cpu_user_properties(Object *cpu_obj)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_misa_ext_cfgs); i++) {
|
||||
KVMCPUConfig *misa_cfg = &kvm_misa_ext_cfgs[i];
|
||||
int bit = misa_cfg->offset;
|
||||
|
||||
misa_cfg->name = riscv_get_misa_ext_name(bit);
|
||||
misa_cfg->description = riscv_get_misa_ext_description(bit);
|
||||
|
||||
object_property_add(cpu_obj, misa_cfg->name, "bool",
|
||||
NULL,
|
||||
kvm_cpu_set_misa_ext_cfg,
|
||||
NULL, misa_cfg);
|
||||
object_property_set_description(cpu_obj, misa_cfg->name,
|
||||
misa_cfg->description);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
|
||||
KVMCPUConfig *multi_cfg = &kvm_multi_ext_cfgs[i];
|
||||
|
||||
object_property_add(cpu_obj, multi_cfg->name, "bool",
|
||||
NULL,
|
||||
kvm_cpu_set_multi_ext_cfg,
|
||||
NULL, multi_cfg);
|
||||
}
|
||||
|
||||
object_property_add(cpu_obj, "cbom_blocksize", "uint16",
|
||||
NULL, kvm_cpu_set_cbomz_blksize,
|
||||
NULL, &kvm_cbom_blocksize);
|
||||
|
||||
object_property_add(cpu_obj, "cboz_blocksize", "uint16",
|
||||
NULL, kvm_cpu_set_cbomz_blksize,
|
||||
NULL, &kvm_cboz_blocksize);
|
||||
}
|
||||
|
||||
static int kvm_riscv_get_regs_core(CPUState *cs)
|
||||
{
|
||||
int ret = 0;
|
||||
@ -309,6 +579,191 @@ static void kvm_riscv_put_regs_timer(CPUState *cs)
|
||||
env->kvm_timer_dirty = false;
|
||||
}
|
||||
|
||||
typedef struct KVMScratchCPU {
|
||||
int kvmfd;
|
||||
int vmfd;
|
||||
int cpufd;
|
||||
} KVMScratchCPU;
|
||||
|
||||
/*
|
||||
* Heavily inspired by kvm_arm_create_scratch_host_vcpu()
|
||||
* from target/arm/kvm.c.
|
||||
*/
|
||||
static bool kvm_riscv_create_scratch_vcpu(KVMScratchCPU *scratch)
|
||||
{
|
||||
int kvmfd = -1, vmfd = -1, cpufd = -1;
|
||||
|
||||
kvmfd = qemu_open_old("/dev/kvm", O_RDWR);
|
||||
if (kvmfd < 0) {
|
||||
goto err;
|
||||
}
|
||||
do {
|
||||
vmfd = ioctl(kvmfd, KVM_CREATE_VM, 0);
|
||||
} while (vmfd == -1 && errno == EINTR);
|
||||
if (vmfd < 0) {
|
||||
goto err;
|
||||
}
|
||||
cpufd = ioctl(vmfd, KVM_CREATE_VCPU, 0);
|
||||
if (cpufd < 0) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
scratch->kvmfd = kvmfd;
|
||||
scratch->vmfd = vmfd;
|
||||
scratch->cpufd = cpufd;
|
||||
|
||||
return true;
|
||||
|
||||
err:
|
||||
if (cpufd >= 0) {
|
||||
close(cpufd);
|
||||
}
|
||||
if (vmfd >= 0) {
|
||||
close(vmfd);
|
||||
}
|
||||
if (kvmfd >= 0) {
|
||||
close(kvmfd);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void kvm_riscv_destroy_scratch_vcpu(KVMScratchCPU *scratch)
|
||||
{
|
||||
close(scratch->cpufd);
|
||||
close(scratch->vmfd);
|
||||
close(scratch->kvmfd);
|
||||
}
|
||||
|
||||
static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
struct kvm_one_reg reg;
|
||||
int ret;
|
||||
|
||||
reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(mvendorid));
|
||||
reg.addr = (uint64_t)&cpu->cfg.mvendorid;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to retrieve mvendorid from host, error %d", ret);
|
||||
}
|
||||
|
||||
reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(marchid));
|
||||
reg.addr = (uint64_t)&cpu->cfg.marchid;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to retrieve marchid from host, error %d", ret);
|
||||
}
|
||||
|
||||
reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(mimpid));
|
||||
reg.addr = (uint64_t)&cpu->cfg.mimpid;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to retrieve mimpid from host, error %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu,
|
||||
KVMScratchCPU *kvmcpu)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
struct kvm_one_reg reg;
|
||||
int ret;
|
||||
|
||||
reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(isa));
|
||||
reg.addr = (uint64_t)&env->misa_ext_mask;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
|
||||
|
||||
if (ret) {
|
||||
error_report("Unable to fetch ISA register from KVM, "
|
||||
"error %d", ret);
|
||||
kvm_riscv_destroy_scratch_vcpu(kvmcpu);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
env->misa_ext = env->misa_ext_mask;
|
||||
}
|
||||
|
||||
static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu,
|
||||
KVMCPUConfig *cbomz_cfg)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
struct kvm_one_reg reg;
|
||||
int ret;
|
||||
|
||||
reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
cbomz_cfg->kvm_reg_id);
|
||||
reg.addr = (uint64_t)kvmconfig_get_cfg_addr(cpu, cbomz_cfg);
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
error_report("Unable to read KVM reg %s, error %d",
|
||||
cbomz_cfg->name, ret);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
uint64_t val;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) {
|
||||
KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i];
|
||||
struct kvm_one_reg reg;
|
||||
|
||||
reg.id = kvm_riscv_reg_id(env, KVM_REG_RISCV_ISA_EXT,
|
||||
multi_ext_cfg->kvm_reg_id);
|
||||
reg.addr = (uint64_t)&val;
|
||||
ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®);
|
||||
if (ret != 0) {
|
||||
if (errno == EINVAL) {
|
||||
/* Silently default to 'false' if KVM does not support it. */
|
||||
multi_ext_cfg->supported = false;
|
||||
val = false;
|
||||
} else {
|
||||
error_report("Unable to read ISA_EXT KVM register %s, "
|
||||
"error %d", multi_ext_cfg->name, ret);
|
||||
kvm_riscv_destroy_scratch_vcpu(kvmcpu);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
multi_ext_cfg->supported = true;
|
||||
}
|
||||
|
||||
kvm_cpu_cfg_set(cpu, multi_ext_cfg, val);
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_icbom) {
|
||||
kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cbom_blocksize);
|
||||
}
|
||||
|
||||
if (cpu->cfg.ext_icboz) {
|
||||
kvm_riscv_read_cbomz_blksize(cpu, kvmcpu, &kvm_cboz_blocksize);
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_riscv_init_user_properties(Object *cpu_obj)
|
||||
{
|
||||
RISCVCPU *cpu = RISCV_CPU(cpu_obj);
|
||||
KVMScratchCPU kvmcpu;
|
||||
|
||||
if (!kvm_riscv_create_scratch_vcpu(&kvmcpu)) {
|
||||
return;
|
||||
}
|
||||
|
||||
kvm_riscv_add_cpu_user_properties(cpu_obj);
|
||||
kvm_riscv_init_machine_ids(cpu, &kvmcpu);
|
||||
kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu);
|
||||
kvm_riscv_init_multiext_cfg(cpu, &kvmcpu);
|
||||
|
||||
kvm_riscv_destroy_scratch_vcpu(&kvmcpu);
|
||||
}
|
||||
|
||||
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
};
|
||||
@ -394,23 +849,49 @@ void kvm_arch_init_irq_routing(KVMState *s)
|
||||
{
|
||||
}
|
||||
|
||||
static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs)
|
||||
{
|
||||
CPURISCVState *env = &cpu->env;
|
||||
uint64_t id;
|
||||
int ret;
|
||||
|
||||
id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(mvendorid));
|
||||
ret = kvm_set_one_reg(cs, id, &cpu->cfg.mvendorid);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(marchid));
|
||||
ret = kvm_set_one_reg(cs, id, &cpu->cfg.marchid);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(mimpid));
|
||||
ret = kvm_set_one_reg(cs, id, &cpu->cfg.mimpid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int kvm_arch_init_vcpu(CPUState *cs)
|
||||
{
|
||||
int ret = 0;
|
||||
target_ulong isa;
|
||||
RISCVCPU *cpu = RISCV_CPU(cs);
|
||||
CPURISCVState *env = &cpu->env;
|
||||
uint64_t id;
|
||||
|
||||
qemu_add_vm_change_state_handler(kvm_riscv_vm_state_change, cs);
|
||||
|
||||
id = kvm_riscv_reg_id(env, KVM_REG_RISCV_CONFIG,
|
||||
KVM_REG_RISCV_CONFIG_REG(isa));
|
||||
ret = kvm_get_one_reg(cs, id, &isa);
|
||||
if (ret) {
|
||||
return ret;
|
||||
if (!object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_CPU_HOST)) {
|
||||
ret = kvm_vcpu_set_machine_ids(cpu, cs);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
env->misa_ext = isa;
|
||||
|
||||
kvm_riscv_update_cpu_misa_ext(cpu, cs);
|
||||
kvm_riscv_update_cpu_cfg_isa_ext(cpu, cs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#ifndef QEMU_KVM_RISCV_H
|
||||
#define QEMU_KVM_RISCV_H
|
||||
|
||||
void kvm_riscv_init_user_properties(Object *cpu_obj);
|
||||
void kvm_riscv_reset_vcpu(RISCVCPU *cpu);
|
||||
void kvm_riscv_set_irq(RISCVCPU *cpu, int irq, int level);
|
||||
|
||||
|
@ -335,7 +335,8 @@ target_ulong helper_mret(CPURISCVState *env)
|
||||
riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC());
|
||||
}
|
||||
|
||||
target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV);
|
||||
target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) &&
|
||||
(prev_priv != PRV_M);
|
||||
mstatus = set_field(mstatus, MSTATUS_MIE,
|
||||
get_field(mstatus, MSTATUS_MPIE));
|
||||
mstatus = set_field(mstatus, MSTATUS_MPIE, 1);
|
||||
|
@ -64,6 +64,7 @@ typedef struct DisasContext {
|
||||
target_ulong priv_ver;
|
||||
RISCVMXL misa_mxl_max;
|
||||
RISCVMXL xl;
|
||||
RISCVMXL address_xl;
|
||||
uint32_t misa_ext;
|
||||
uint32_t opcode;
|
||||
RISCVExtStatus mstatus_fs;
|
||||
@ -121,29 +122,6 @@ static inline bool has_ext(DisasContext *ctx, uint32_t ext)
|
||||
return ctx->misa_ext & ext;
|
||||
}
|
||||
|
||||
static bool always_true_p(DisasContext *ctx __attribute__((__unused__)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool has_xthead_p(DisasContext *ctx __attribute__((__unused__)))
|
||||
{
|
||||
return ctx->cfg_ptr->ext_xtheadba || ctx->cfg_ptr->ext_xtheadbb ||
|
||||
ctx->cfg_ptr->ext_xtheadbs || ctx->cfg_ptr->ext_xtheadcmo ||
|
||||
ctx->cfg_ptr->ext_xtheadcondmov ||
|
||||
ctx->cfg_ptr->ext_xtheadfmemidx || ctx->cfg_ptr->ext_xtheadfmv ||
|
||||
ctx->cfg_ptr->ext_xtheadmac || ctx->cfg_ptr->ext_xtheadmemidx ||
|
||||
ctx->cfg_ptr->ext_xtheadmempair || ctx->cfg_ptr->ext_xtheadsync;
|
||||
}
|
||||
|
||||
#define MATERIALISE_EXT_PREDICATE(ext) \
|
||||
static bool has_ ## ext ## _p(DisasContext *ctx) \
|
||||
{ \
|
||||
return ctx->cfg_ptr->ext_ ## ext ; \
|
||||
}
|
||||
|
||||
MATERIALISE_EXT_PREDICATE(XVentanaCondOps);
|
||||
|
||||
#ifdef TARGET_RISCV32
|
||||
#define get_xl(ctx) MXL_RV32
|
||||
#elif defined(CONFIG_USER_ONLY)
|
||||
@ -152,6 +130,14 @@ MATERIALISE_EXT_PREDICATE(XVentanaCondOps);
|
||||
#define get_xl(ctx) ((ctx)->xl)
|
||||
#endif
|
||||
|
||||
#ifdef TARGET_RISCV32
|
||||
#define get_address_xl(ctx) MXL_RV32
|
||||
#elif defined(CONFIG_USER_ONLY)
|
||||
#define get_address_xl(ctx) MXL_RV64
|
||||
#else
|
||||
#define get_address_xl(ctx) ((ctx)->address_xl)
|
||||
#endif
|
||||
|
||||
/* The word size for this machine mode. */
|
||||
static inline int __attribute__((unused)) get_xlen(DisasContext *ctx)
|
||||
{
|
||||
@ -598,12 +584,13 @@ static TCGv get_address(DisasContext *ctx, int rs1, int imm)
|
||||
tcg_gen_addi_tl(addr, src1, imm);
|
||||
if (ctx->pm_mask_enabled) {
|
||||
tcg_gen_andc_tl(addr, addr, pm_mask);
|
||||
} else if (get_xl(ctx) == MXL_RV32) {
|
||||
} else if (get_address_xl(ctx) == MXL_RV32) {
|
||||
tcg_gen_ext32u_tl(addr, addr);
|
||||
}
|
||||
if (ctx->pm_base_enabled) {
|
||||
tcg_gen_or_tl(addr, addr, pm_base);
|
||||
}
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
@ -1104,10 +1091,12 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc)
|
||||
#include "insn_trans/trans_rvzicond.c.inc"
|
||||
#include "insn_trans/trans_rvzawrs.c.inc"
|
||||
#include "insn_trans/trans_rvzicbo.c.inc"
|
||||
#include "insn_trans/trans_rvzfa.c.inc"
|
||||
#include "insn_trans/trans_rvzfh.c.inc"
|
||||
#include "insn_trans/trans_rvk.c.inc"
|
||||
#include "insn_trans/trans_privileged.c.inc"
|
||||
#include "insn_trans/trans_svinval.c.inc"
|
||||
#include "insn_trans/trans_rvbf16.c.inc"
|
||||
#include "decode-xthead.c.inc"
|
||||
#include "insn_trans/trans_xthead.c.inc"
|
||||
#include "insn_trans/trans_xventanacondops.c.inc"
|
||||
@ -1134,7 +1123,7 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
|
||||
* that are tested in-order until a decoder matches onto the opcode.
|
||||
*/
|
||||
static const struct {
|
||||
bool (*guard_func)(DisasContext *);
|
||||
bool (*guard_func)(const RISCVCPUConfig *);
|
||||
bool (*decode_func)(DisasContext *, uint32_t);
|
||||
} decoders[] = {
|
||||
{ always_true_p, decode_insn32 },
|
||||
@ -1163,7 +1152,7 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode)
|
||||
ctx->opcode = opcode32;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(decoders); ++i) {
|
||||
if (decoders[i].guard_func(ctx) &&
|
||||
if (decoders[i].guard_func(ctx->cfg_ptr) &&
|
||||
decoders[i].decode_func(ctx, opcode32)) {
|
||||
return;
|
||||
}
|
||||
@ -1200,6 +1189,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
|
||||
ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX);
|
||||
ctx->misa_mxl_max = env->misa_mxl_max;
|
||||
ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL);
|
||||
ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL);
|
||||
ctx->cs = cs;
|
||||
ctx->pm_mask_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_MASK_ENABLED);
|
||||
ctx->pm_base_enabled = FIELD_EX32(tb_flags, TB_FLAGS, PM_BASE_ENABLED);
|
||||
|
@ -3554,6 +3554,17 @@ RVVCALL(OPFVF3, vfwmacc_vf_w, WOP_UUU_W, H8, H4, fwmacc32)
|
||||
GEN_VEXT_VF(vfwmacc_vf_h, 4)
|
||||
GEN_VEXT_VF(vfwmacc_vf_w, 8)
|
||||
|
||||
static uint32_t fwmaccbf16(uint16_t a, uint16_t b, uint32_t d, float_status *s)
|
||||
{
|
||||
return float32_muladd(bfloat16_to_float32(a, s),
|
||||
bfloat16_to_float32(b, s), d, 0, s);
|
||||
}
|
||||
|
||||
RVVCALL(OPFVV3, vfwmaccbf16_vv, WOP_UUU_H, H4, H2, H2, fwmaccbf16)
|
||||
GEN_VEXT_VV_ENV(vfwmaccbf16_vv, 4)
|
||||
RVVCALL(OPFVF3, vfwmaccbf16_vf, WOP_UUU_H, H4, H2, fwmacc16)
|
||||
GEN_VEXT_VF(vfwmaccbf16_vf, 4)
|
||||
|
||||
static uint32_t fwnmacc16(uint16_t a, uint16_t b, uint32_t d, float_status *s)
|
||||
{
|
||||
return float32_muladd(float16_to_float32(a, true, s),
|
||||
@ -4535,6 +4546,9 @@ RVVCALL(OPFVV1, vfwcvt_f_f_v_w, WOP_UU_W, H8, H4, float32_to_float64)
|
||||
GEN_VEXT_V_ENV(vfwcvt_f_f_v_h, 4)
|
||||
GEN_VEXT_V_ENV(vfwcvt_f_f_v_w, 8)
|
||||
|
||||
RVVCALL(OPFVV1, vfwcvtbf16_f_f_v, WOP_UU_H, H4, H2, bfloat16_to_float32)
|
||||
GEN_VEXT_V_ENV(vfwcvtbf16_f_f_v, 4)
|
||||
|
||||
/* Narrowing Floating-Point/Integer Type-Convert Instructions */
|
||||
/* (TD, T2, TX2) */
|
||||
#define NOP_UU_B uint8_t, uint16_t, uint32_t
|
||||
@ -4581,6 +4595,9 @@ RVVCALL(OPFVV1, vfncvt_f_f_w_w, NOP_UU_W, H4, H8, float64_to_float32)
|
||||
GEN_VEXT_V_ENV(vfncvt_f_f_w_h, 2)
|
||||
GEN_VEXT_V_ENV(vfncvt_f_f_w_w, 4)
|
||||
|
||||
RVVCALL(OPFVV1, vfncvtbf16_f_f_w, NOP_UU_H, H2, H4, float32_to_bfloat16)
|
||||
GEN_VEXT_V_ENV(vfncvtbf16_f_f_w, 2)
|
||||
|
||||
/*
|
||||
* Vector Reduction Operations
|
||||
*/
|
||||
|
@ -6,7 +6,6 @@
|
||||
# later. See the COPYING file in the top-level directory.
|
||||
|
||||
from avocado_qemu import QemuSystemTest
|
||||
from avocado import skip
|
||||
from avocado_qemu import wait_for_console_pattern
|
||||
|
||||
class RiscvOpenSBI(QemuSystemTest):
|
||||
@ -21,7 +20,6 @@ class RiscvOpenSBI(QemuSystemTest):
|
||||
wait_for_console_pattern(self, 'Platform Name')
|
||||
wait_for_console_pattern(self, 'Boot HART MEDELEG')
|
||||
|
||||
@skip("requires OpenSBI fix to work")
|
||||
def test_riscv32_spike(self):
|
||||
"""
|
||||
:avocado: tags=arch:riscv32
|
||||
|
@ -234,6 +234,9 @@ qtests_s390x = \
|
||||
'cpu-plug-test',
|
||||
'migration-test']
|
||||
|
||||
qtests_riscv32 = \
|
||||
(config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : [])
|
||||
|
||||
qos_test_ss = ss.source_set()
|
||||
qos_test_ss.add(
|
||||
'ac97-test.c',
|
||||
|
450
tests/qtest/sifive-e-aon-watchdog-test.c
Normal file
450
tests/qtest/sifive-e-aon-watchdog-test.c
Normal file
@ -0,0 +1,450 @@
|
||||
/*
|
||||
* QTest testcase for the watchdog timer of HiFive 1 rev b.
|
||||
*
|
||||
* Copyright (c) 2023 SiFive, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2 or later, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "libqtest.h"
|
||||
#include "hw/registerfields.h"
|
||||
#include "hw/misc/sifive_e_aon.h"
|
||||
|
||||
FIELD(AON_WDT_WDOGCFG, SCALE, 0, 4)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD0, 4, 4)
|
||||
FIELD(AON_WDT_WDOGCFG, RSTEN, 8, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, ZEROCMP, 9, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD1, 10, 2)
|
||||
FIELD(AON_WDT_WDOGCFG, EN_ALWAYS, 12, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, EN_CORE_AWAKE, 13, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD2, 14, 14)
|
||||
FIELD(AON_WDT_WDOGCFG, IP0, 28, 1)
|
||||
FIELD(AON_WDT_WDOGCFG, RSVD3, 29, 3)
|
||||
|
||||
#define WDOG_BASE (0x10000000)
|
||||
#define WDOGCFG (0x0)
|
||||
#define WDOGCOUNT (0x8)
|
||||
#define WDOGS (0x10)
|
||||
#define WDOGFEED (0x18)
|
||||
#define WDOGKEY (0x1c)
|
||||
#define WDOGCMP0 (0x20)
|
||||
|
||||
#define SIFIVE_E_AON_WDOGKEY (0x51F15E)
|
||||
#define SIFIVE_E_AON_WDOGFEED (0xD09F00D)
|
||||
#define SIFIVE_E_LFCLK_DEFAULT_FREQ (32768)
|
||||
|
||||
static void test_init(QTestState *qts)
|
||||
{
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, 0);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF);
|
||||
}
|
||||
|
||||
static void test_wdogcount(void)
|
||||
{
|
||||
uint64_t tmp;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
tmp = qtest_readl(qts, WDOG_BASE + WDOGCOUNT);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF);
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == tmp);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xBEEF);
|
||||
g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0xAAAAAAAA);
|
||||
g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA);
|
||||
g_assert(0x2AAAAAAA == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGFEED, SIFIVE_E_AON_WDOGFEED);
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_wdogcfg(void)
|
||||
{
|
||||
uint32_t tmp_cfg;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF);
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCFG) == tmp_cfg);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, 0xFFFFFFFF);
|
||||
g_assert(0xFFFFFFFF == qtest_readl(qts, WDOG_BASE + WDOGCFG));
|
||||
|
||||
tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(15 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(1 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, 0);
|
||||
tmp_cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(0 == FIELD_EX32(tmp_cfg, AON_WDT_WDOGCFG, IP0));
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCFG));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_wdogcmp0(void)
|
||||
{
|
||||
uint32_t tmp;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
tmp = qtest_readl(qts, WDOG_BASE + WDOGCMP0);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF);
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCMP0) == tmp);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCMP0, 0xBEEF);
|
||||
g_assert(0xBEEF == qtest_readl(qts, WDOG_BASE + WDOGCMP0));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_wdogkey(void)
|
||||
{
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, 0xFFFF);
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
g_assert(1 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xAAAAAAAA);
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGKEY));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_wdogfeed(void)
|
||||
{
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGFEED, 0xFFFF);
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGFEED));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_scaled_wdogs(void)
|
||||
{
|
||||
uint32_t cfg;
|
||||
uint32_t fake_count = 0x12345678;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, fake_count);
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) == fake_count);
|
||||
g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) ==
|
||||
(uint16_t)fake_count);
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, i);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
g_assert((uint16_t)qtest_readl(qts, WDOG_BASE + WDOGS) ==
|
||||
(uint16_t)(fake_count >>
|
||||
FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE)));
|
||||
}
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_watchdog(void)
|
||||
{
|
||||
uint32_t cfg;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ);
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
|
||||
qtest_clock_step(qts, NANOSECONDS_PER_SECOND);
|
||||
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
|
||||
SIFIVE_E_LFCLK_DEFAULT_FREQ);
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGS) ==
|
||||
SIFIVE_E_LFCLK_DEFAULT_FREQ);
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_scaled_watchdog(void)
|
||||
{
|
||||
uint32_t cfg;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10);
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
|
||||
qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 10);
|
||||
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
|
||||
SIFIVE_E_LFCLK_DEFAULT_FREQ * 10);
|
||||
|
||||
g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS));
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_periodic_int(void)
|
||||
{
|
||||
uint32_t cfg;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCMP0, SIFIVE_E_LFCLK_DEFAULT_FREQ);
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 0);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, ZEROCMP, 1);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
|
||||
qtest_clock_step(qts, NANOSECONDS_PER_SECOND);
|
||||
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS));
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_clock_step(qts, NANOSECONDS_PER_SECOND);
|
||||
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGCOUNT));
|
||||
g_assert(0 == qtest_readl(qts, WDOG_BASE + WDOGS));
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
static void test_enable_disable(void)
|
||||
{
|
||||
uint32_t cfg;
|
||||
QTestState *qts = qtest_init("-machine sifive_e");
|
||||
|
||||
test_init(qts);
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCMP0, 10);
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, SCALE, 15);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
|
||||
qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 2);
|
||||
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
|
||||
SIFIVE_E_LFCLK_DEFAULT_FREQ * 2);
|
||||
g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS));
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 0);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
|
||||
qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8);
|
||||
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
|
||||
SIFIVE_E_LFCLK_DEFAULT_FREQ * 2);
|
||||
g_assert(2 == qtest_readl(qts, WDOG_BASE + WDOGS));
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS, 1);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
|
||||
qtest_clock_step(qts, NANOSECONDS_PER_SECOND * 8);
|
||||
|
||||
g_assert(qtest_readl(qts, WDOG_BASE + WDOGCOUNT) ==
|
||||
SIFIVE_E_LFCLK_DEFAULT_FREQ * 10);
|
||||
g_assert(10 == qtest_readl(qts, WDOG_BASE + WDOGS));
|
||||
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(15 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, SCALE));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, RSTEN));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, ZEROCMP));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_ALWAYS));
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, EN_CORE_AWAKE));
|
||||
g_assert(1 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCOUNT, 0);
|
||||
cfg = FIELD_DP32(cfg, AON_WDT_WDOGCFG, IP0, 0);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGKEY, SIFIVE_E_AON_WDOGKEY);
|
||||
qtest_writel(qts, WDOG_BASE + WDOGCFG, cfg);
|
||||
cfg = qtest_readl(qts, WDOG_BASE + WDOGCFG);
|
||||
g_assert(0 == FIELD_EX32(cfg, AON_WDT_WDOGCFG, IP0));
|
||||
|
||||
qtest_quit(qts);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/wdogcount",
|
||||
test_wdogcount);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/wdogcfg",
|
||||
test_wdogcfg);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/wdogcmp0",
|
||||
test_wdogcmp0);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/wdogkey",
|
||||
test_wdogkey);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/wdogfeed",
|
||||
test_wdogfeed);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/scaled_wdogs",
|
||||
test_scaled_wdogs);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/watchdog",
|
||||
test_watchdog);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/scaled_watchdog",
|
||||
test_scaled_watchdog);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/periodic_int",
|
||||
test_periodic_int);
|
||||
qtest_add_func("/sifive-e-aon-watchdog-test/enable_disable",
|
||||
test_enable_disable);
|
||||
return g_test_run();
|
||||
}
|
@ -12,3 +12,9 @@ run-test-noc: QEMU_OPTS += -cpu rv64,c=false
|
||||
|
||||
TESTS += test-aes
|
||||
run-test-aes: QEMU_OPTS += -cpu rv64,zk=on
|
||||
|
||||
# Test for fcvtmod
|
||||
TESTS += test-fcvtmod
|
||||
test-fcvtmod: CFLAGS += -march=rv64imafdc
|
||||
test-fcvtmod: LDFLAGS += -static
|
||||
run-test-fcvtmod: QEMU_OPTS += -cpu rv64,d=true,Zfa=true
|
||||
|
345
tests/tcg/riscv64/test-fcvtmod.c
Normal file
345
tests/tcg/riscv64/test-fcvtmod.c
Normal file
@ -0,0 +1,345 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define FFLAG_NX_SHIFT 0 /* inexact */
|
||||
#define FFLAG_UF_SHIFT 1 /* underflow */
|
||||
#define FFLAG_OF_SHIFT 2 /* overflow */
|
||||
#define FFLAG_DZ_SHIFT 3 /* divide by zero */
|
||||
#define FFLAG_NV_SHIFT 4 /* invalid operation */
|
||||
|
||||
#define FFLAG_NV (1UL << FFLAG_NV_SHIFT)
|
||||
#define FFLAG_DZ (1UL << FFLAG_DZ_SHIFT)
|
||||
#define FFLAG_OF (1UL << FFLAG_OF_SHIFT)
|
||||
#define FFLAG_UF (1UL << FFLAG_UF_SHIFT)
|
||||
#define FFLAG_NX (1UL << FFLAG_NX_SHIFT)
|
||||
|
||||
typedef struct fp64_fcvt_fcvtmod_testcase {
|
||||
const char* name;
|
||||
union {
|
||||
uint64_t inp_lu;
|
||||
double inp_lf;
|
||||
};
|
||||
uint64_t exp_fcvt;
|
||||
uint8_t exp_fcvt_fflags;
|
||||
uint64_t exp_fcvtmod;
|
||||
uint8_t exp_fcvtmod_fflags;
|
||||
} fp64_fcvt_fcvtmod_testcase_t;
|
||||
|
||||
void print_fflags(uint8_t fflags)
|
||||
{
|
||||
int set = 0;
|
||||
|
||||
if (fflags == 0) {
|
||||
printf("-");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fflags & FFLAG_NV) {
|
||||
printf("%sFFLAG_NV", set ? " | " : "");
|
||||
set = 1;
|
||||
}
|
||||
if (fflags & FFLAG_DZ) {
|
||||
printf("%sFFLAG_DZ", set ? " | " : "");
|
||||
set = 1;
|
||||
}
|
||||
if (fflags & FFLAG_OF) {
|
||||
printf("%sFFLAG_OF", set ? " | " : "");
|
||||
set = 1;
|
||||
}
|
||||
if (fflags & FFLAG_UF) {
|
||||
printf("%sFFLAG_UF", set ? " | " : "");
|
||||
set = 1;
|
||||
}
|
||||
if (fflags & FFLAG_NX) {
|
||||
printf("%sFFLAG_NX", set ? " | " : "");
|
||||
set = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear all FP flags. */
|
||||
static inline void clear_fflags()
|
||||
{
|
||||
__asm__ __volatile__("fsflags zero");
|
||||
}
|
||||
|
||||
/* Read all FP flags. */
|
||||
static inline uint8_t get_fflags()
|
||||
{
|
||||
uint64_t v;
|
||||
__asm__ __volatile__("frflags %0" : "=r"(v));
|
||||
return (uint8_t)v;
|
||||
}
|
||||
|
||||
/* Move input value (without conversations) into an FP register. */
|
||||
static inline double do_fmv_d_x(uint64_t inp)
|
||||
{
|
||||
double fpr;
|
||||
__asm__ __volatile__("fmv.d.x %0, %1" : "=f"(fpr) : "r"(inp));
|
||||
return fpr;
|
||||
}
|
||||
|
||||
static inline uint64_t do_fcvt_w_d(uint64_t inp, uint8_t *fflags)
|
||||
{
|
||||
uint64_t ret;
|
||||
double fpr = do_fmv_d_x(inp);
|
||||
|
||||
clear_fflags();
|
||||
|
||||
__asm__ __volatile__("fcvt.w.d %0, %1, rtz" : "=r"(ret) : "f"(fpr));
|
||||
|
||||
*fflags = get_fflags();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline uint64_t do_fcvtmod_w_d(uint64_t inp, uint8_t *fflags)
|
||||
{
|
||||
uint64_t ret;
|
||||
double fpr = do_fmv_d_x(inp);
|
||||
|
||||
clear_fflags();
|
||||
|
||||
/* fcvtmod.w.d rd, rs1, rtz = 1100001 01000 rs1 001 rd 1010011 */
|
||||
asm(".insn r 0x53, 0x1, 0x61, %0, %1, f8" : "=r"(ret) : "f"(fpr));
|
||||
|
||||
*fflags = get_fflags();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const fp64_fcvt_fcvtmod_testcase_t tests[] = {
|
||||
/* Zero (exp=0, frac=0) */
|
||||
{ .name = "+0.0",
|
||||
.inp_lf = 0x0p0,
|
||||
.exp_fcvt = 0x0000000000000000,
|
||||
.exp_fcvt_fflags = 0,
|
||||
.exp_fcvtmod = 0x0000000000000000,
|
||||
.exp_fcvtmod_fflags = 0 },
|
||||
{ .name = "-0.0",
|
||||
.inp_lf = -0x0p0,
|
||||
.exp_fcvt = 0x0000000000000000,
|
||||
.exp_fcvt_fflags = 0,
|
||||
.exp_fcvtmod = 0x0000000000000000,
|
||||
.exp_fcvtmod_fflags = 0 },
|
||||
|
||||
/* Subnormal: exp=0 frac!=0 */
|
||||
{ .name = "Subnormal frac=1",
|
||||
.inp_lu = 0x0000000000000001,
|
||||
.exp_fcvt = 0x0000000000000000,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
{ .name = "Subnormal frac=0xf..f",
|
||||
.inp_lu = 0x0000ffffffffffff,
|
||||
.exp_fcvt = 0x0000000000000000,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
{ .name = "Neg subnormal frac=1",
|
||||
.inp_lu = 0x0000000000000001,
|
||||
.exp_fcvt = 0x0000000000000000,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
{ .name = "Neg subnormal frac=0xf..f",
|
||||
.inp_lu = 0x8000ffffffffffff,
|
||||
.exp_fcvt = 0x0000000000000000,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
|
||||
/* Infinity: exp=0x7ff, frac=0 */
|
||||
{ .name = "+INF",
|
||||
.inp_lu = 0x7ff0000000000000,
|
||||
.exp_fcvt = 0x000000007fffffff, /* int32 max */
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
{ .name = "-INF",
|
||||
.inp_lu = 0xfff0000000000000,
|
||||
.exp_fcvt = 0xffffffff80000000, /* int32 min */
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
|
||||
/* NaN: exp=7ff, frac!=0 */
|
||||
{ .name = "canonical NaN",
|
||||
.inp_lu = 0x7ff8000000000000,
|
||||
.exp_fcvt = 0x000000007fffffff, /* int32 max */
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
{ .name = "non-canonical NaN",
|
||||
.inp_lu = 0x7ff8000000100000,
|
||||
.exp_fcvt = 0x000000007fffffff, /* int32 min */
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
|
||||
/* Normal numbers: exp!=0, exp!=7ff */
|
||||
{ .name = "+smallest normal value",
|
||||
.inp_lu = 0x0010000000000000,
|
||||
.exp_fcvt = 0,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
{ .name = "-smallest normal value",
|
||||
.inp_lu = 0x8010000000000000,
|
||||
.exp_fcvt = 0,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
|
||||
{ .name = "+0.5",
|
||||
.inp_lf = 0x1p-1,
|
||||
.exp_fcvt = 0,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
{ .name = "-0.5",
|
||||
.inp_lf = -0x1p-1,
|
||||
.exp_fcvt = 0,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
|
||||
{ .name = "+value just below 1.0",
|
||||
.inp_lu = 0x3fefffffffffffff,
|
||||
.exp_fcvt = 0,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
{ .name = "-value just above -1.0",
|
||||
.inp_lu = 0xbfefffffffffffff,
|
||||
.exp_fcvt = 0,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
|
||||
{ .name = "+1.0",
|
||||
.inp_lf = 0x1p0,
|
||||
.exp_fcvt = 0x0000000000000001,
|
||||
.exp_fcvt_fflags = 0,
|
||||
.exp_fcvtmod = 0x0000000000000001,
|
||||
.exp_fcvtmod_fflags = 0 },
|
||||
{ .name = "-1.0",
|
||||
.inp_lf = -0x1p0,
|
||||
.exp_fcvt = 0xffffffffffffffff,
|
||||
.exp_fcvt_fflags = 0,
|
||||
.exp_fcvtmod = 0xffffffffffffffff,
|
||||
.exp_fcvtmod_fflags = 0 },
|
||||
|
||||
{ .name = "+1.5",
|
||||
.inp_lu = 0x3ff8000000000000,
|
||||
.exp_fcvt = 1,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 1,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
{ .name = "-1.5",
|
||||
.inp_lu = 0xbff8000000000000,
|
||||
.exp_fcvt = 0xffffffffffffffff,
|
||||
.exp_fcvt_fflags = FFLAG_NX,
|
||||
.exp_fcvtmod = 0xffffffffffffffff,
|
||||
.exp_fcvtmod_fflags = FFLAG_NX },
|
||||
|
||||
{ .name = "+max int32 (2147483647)",
|
||||
.inp_lu = 0x41dfffffffc00000,
|
||||
.exp_fcvt = 0x000000007fffffff,
|
||||
.exp_fcvt_fflags = 0,
|
||||
.exp_fcvtmod = 0x000000007fffffff,
|
||||
.exp_fcvtmod_fflags = 0 },
|
||||
{ .name = "+max int32 +1 (2147483648)",
|
||||
.inp_lf = 0x1p31,
|
||||
.exp_fcvt = 0x000000007fffffff,
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = (uint64_t)-2147483648l, /* int32 min */
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
{ .name = "+max int32 +2 (2147483649)",
|
||||
.inp_lu = 0x41e0000000200000,
|
||||
.exp_fcvt = 0x000000007fffffff,
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = (uint64_t)-2147483647l, /* int32 min +1 */
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
|
||||
{ .name = "-max int32 (-2147483648)",
|
||||
.inp_lf = -0x1p31,
|
||||
.exp_fcvt = 0xffffffff80000000,
|
||||
.exp_fcvt_fflags = 0,
|
||||
.exp_fcvtmod = 0xffffffff80000000,
|
||||
.exp_fcvtmod_fflags = 0 },
|
||||
{ .name = "-max int32 -1 (-2147483649)",
|
||||
.inp_lf = -0x1.00000002p+31,
|
||||
.exp_fcvt = 0xffffffff80000000,
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = 2147483647, /* int32 max */
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
{ .name = "-max int32 -2 (-2147483650)",
|
||||
.inp_lf = -0x1.00000004p+31,
|
||||
.exp_fcvt = 0xffffffff80000000,
|
||||
.exp_fcvt_fflags = FFLAG_NV,
|
||||
.exp_fcvtmod = 2147483646, /* int32 max -1 */
|
||||
.exp_fcvtmod_fflags = FFLAG_NV },
|
||||
};
|
||||
|
||||
int run_fcvtmod_tests()
|
||||
{
|
||||
uint64_t act_fcvt;
|
||||
uint8_t act_fcvt_fflags;
|
||||
uint64_t act_fcvtmod;
|
||||
uint8_t act_fcvtmod_fflags;
|
||||
|
||||
for (size_t i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
|
||||
const fp64_fcvt_fcvtmod_testcase_t *t = &tests[i];
|
||||
|
||||
act_fcvt = do_fcvt_w_d(t->inp_lu, &act_fcvt_fflags);
|
||||
int fcvt_correct = act_fcvt == t->exp_fcvt &&
|
||||
act_fcvt_fflags == t->exp_fcvt_fflags;
|
||||
act_fcvtmod = do_fcvtmod_w_d(t->inp_lu, &act_fcvtmod_fflags);
|
||||
int fcvtmod_correct = act_fcvtmod == t->exp_fcvtmod &&
|
||||
act_fcvtmod_fflags == t->exp_fcvtmod_fflags;
|
||||
|
||||
if (fcvt_correct && fcvtmod_correct) {
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("Test %zu (%s) failed!\n", i, t->name);
|
||||
|
||||
double fpr = do_fmv_d_x(t->inp_lu);
|
||||
printf("inp_lu: 0x%016lx == %lf\n", t->inp_lu, fpr);
|
||||
printf("inp_lf: %lf\n", t->inp_lf);
|
||||
|
||||
uint32_t sign = (t->inp_lu >> 63);
|
||||
uint32_t exp = (uint32_t)(t->inp_lu >> 52) & 0x7ff;
|
||||
uint64_t frac = t->inp_lu & 0xfffffffffffffull; /* significand */
|
||||
int true_exp = exp - 1023;
|
||||
int shift = true_exp - 52;
|
||||
uint64_t true_frac = frac | 1ull << 52;
|
||||
|
||||
printf("sign=%d, exp=0x%03x, frac=0x%012lx\n", sign, exp, frac);
|
||||
printf("true_exp=%d, shift=%d, true_frac=0x%016lx\n", true_exp, shift, true_frac);
|
||||
|
||||
if (!fcvt_correct) {
|
||||
printf("act_fcvt: 0x%016lx == %li\n", act_fcvt, act_fcvt);
|
||||
printf("exp_fcvt: 0x%016lx == %li\n", t->exp_fcvt, t->exp_fcvt);
|
||||
printf("act_fcvt_fflags: "); print_fflags(act_fcvt_fflags); printf("\n");
|
||||
printf("exp_fcvt_fflags: "); print_fflags(t->exp_fcvt_fflags); printf("\n");
|
||||
}
|
||||
|
||||
if (!fcvtmod_correct) {
|
||||
printf("act_fcvtmod: 0x%016lx == %li\n", act_fcvtmod, act_fcvtmod);
|
||||
printf("exp_fcvtmod: 0x%016lx == %li\n", t->exp_fcvtmod, t->exp_fcvtmod);
|
||||
printf("act_fcvtmod_fflags: "); print_fflags(act_fcvtmod_fflags); printf("\n");
|
||||
printf("exp_fcvtmod_fflags: "); print_fflags(t->exp_fcvtmod_fflags); printf("\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
return run_fcvtmod_tests();
|
||||
}
|
Loading…
Reference in New Issue
Block a user