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:
Richard Henderson 2023-07-10 21:42:50 +01:00
commit 94d68c1136
44 changed files with 4751 additions and 442 deletions

View File

@ -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
View 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
View 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
View 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
View 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 */

View File

@ -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
View 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 */

View File

@ -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]

View File

@ -158,6 +158,9 @@ config SIFIVE_TEST
config SIFIVE_E_PRCI
bool
config SIFIVE_E_AON
bool
config SIFIVE_U_OTP
bool

View File

@ -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
View 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)

View File

@ -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

View File

@ -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));

View File

@ -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",

View 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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

@ -1 +1 @@
Subproject commit 6b5188ca14e59ce7bf71afe4e7d3d557c3d31bf8
Subproject commit 2552799a1df30a3dcd2321a8b75d61d06f5fb9fc

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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;

View File

@ -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));
}

View File

@ -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)

View File

@ -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

View 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;
}

View 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;
}

View File

@ -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);

View File

@ -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), &reg); \
int ret = kvm_set_one_reg(cs, RISCV_TIMER_REG(env, name), &reg); \
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, &reg);
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, &reg);
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, &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, &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, &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, &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, &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, &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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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
*/

View File

@ -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

View File

@ -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',

View 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();
}

View File

@ -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

View 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();
}