mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:10 +00:00
Signed-off-by: Matheus Tavares Bernardino <quic_mathbern@quicinc.com> Reviewed-by: Taylor Simpson <ltaylorsimpson@gmail.com> Signed-off-by: Brian Cain <brian.cain@oss.qualcomm.com>
1943 lines
57 KiB
C
1943 lines
57 KiB
C
/*
|
|
* Copyright(c) 2019-2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that 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/log.h"
|
|
#include "accel/tcg/cpu-ldst.h"
|
|
#include "accel/tcg/cpu-loop.h"
|
|
#include "accel/tcg/probe.h"
|
|
#include "qemu/main-loop.h"
|
|
#include "cpu.h"
|
|
#include "exec/helper-proto.h"
|
|
#include "fpu/softfloat.h"
|
|
#include "exec/cpu-interrupt.h"
|
|
#include "internal.h"
|
|
#include "macros.h"
|
|
#include "sys_macros.h"
|
|
#include "arch.h"
|
|
#include "hex_arch_types.h"
|
|
#include "fma_emu.h"
|
|
#include "mmvec/mmvec.h"
|
|
#include "mmvec/macros.h"
|
|
#include "op_helper.h"
|
|
#include "cpu_helper.h"
|
|
#include "translate.h"
|
|
#ifndef CONFIG_USER_ONLY
|
|
#include "hw/hexagon/hexagon_globalreg.h"
|
|
#include "hex_mmu.h"
|
|
#include "hw/hexagon/hexagon_tlb.h"
|
|
#include "hex_interrupts.h"
|
|
#include "hexswi.h"
|
|
#endif
|
|
|
|
#define SF_BIAS 127
|
|
#define SF_MANTBITS 23
|
|
|
|
/* Exceptions processing helpers */
|
|
G_NORETURN
|
|
void do_raise_exception(CPUHexagonState *env, uint32_t exception,
|
|
uint32_t PC, uintptr_t retaddr)
|
|
{
|
|
CPUState *cs = env_cpu(env);
|
|
qemu_log_mask(CPU_LOG_INT, "%s: 0x%08" PRIx32 ", @ %08" PRIx32 "\n",
|
|
__func__, exception, PC);
|
|
ASSERT_DIRECT_TO_GUEST_UNSET(env, exception);
|
|
|
|
env->gpr[HEX_REG_PC] = PC;
|
|
cs->exception_index = exception;
|
|
cpu_loop_exit_restore(cs, retaddr);
|
|
}
|
|
|
|
G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env,
|
|
uint32_t exception,
|
|
uintptr_t pc)
|
|
{
|
|
do_raise_exception(env, exception, pc, 0);
|
|
}
|
|
|
|
G_NORETURN void HELPER(raise_exception)(CPUHexagonState *env, uint32_t excp,
|
|
uint32_t PC)
|
|
{
|
|
hexagon_raise_exception_err(env, excp, PC);
|
|
}
|
|
|
|
void log_store32(CPUHexagonState *env, target_ulong addr,
|
|
target_ulong val, uint32_t width, int slot)
|
|
{
|
|
env->mem_log_stores[slot].va = addr;
|
|
env->mem_log_stores[slot].width = width;
|
|
env->mem_log_stores[slot].data32 = val;
|
|
}
|
|
|
|
void log_store64(CPUHexagonState *env, target_ulong addr,
|
|
int64_t val, uint32_t width, int slot)
|
|
{
|
|
env->mem_log_stores[slot].va = addr;
|
|
env->mem_log_stores[slot].width = width;
|
|
env->mem_log_stores[slot].data64 = val;
|
|
}
|
|
|
|
static void commit_store(CPUHexagonState *env, int slot_num, uintptr_t ra)
|
|
{
|
|
uint32_t width = env->mem_log_stores[slot_num].width;
|
|
target_ulong va = env->mem_log_stores[slot_num].va;
|
|
|
|
switch (width) {
|
|
case 1:
|
|
cpu_stb_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra);
|
|
break;
|
|
case 2:
|
|
cpu_stw_le_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra);
|
|
break;
|
|
case 4:
|
|
cpu_stl_le_data_ra(env, va, env->mem_log_stores[slot_num].data32, ra);
|
|
break;
|
|
case 8:
|
|
cpu_stq_le_data_ra(env, va, env->mem_log_stores[slot_num].data64, ra);
|
|
break;
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
}
|
|
|
|
void HELPER(commit_store)(CPUHexagonState *env, int slot_num)
|
|
{
|
|
uintptr_t ra = GETPC();
|
|
commit_store(env, slot_num, ra);
|
|
}
|
|
|
|
void HELPER(gather_store)(CPUHexagonState *env, uint32_t addr, int slot)
|
|
{
|
|
mem_gather_store(env, addr, slot);
|
|
}
|
|
|
|
void HELPER(commit_hvx_stores)(CPUHexagonState *env)
|
|
{
|
|
uintptr_t ra = GETPC();
|
|
|
|
/* Normal (possibly masked) vector store */
|
|
for (int i = 0; i < VSTORES_MAX; i++) {
|
|
if (env->vstore_pending[i]) {
|
|
env->vstore_pending[i] = 0;
|
|
target_ulong va = env->vstore[i].va;
|
|
int size = env->vstore[i].size;
|
|
for (int j = 0; j < size; j++) {
|
|
if (test_bit(j, env->vstore[i].mask)) {
|
|
cpu_stb_data_ra(env, va + j, env->vstore[i].data.ub[j], ra);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scatter store */
|
|
if (env->vtcm_pending) {
|
|
env->vtcm_pending = false;
|
|
if (env->vtcm_log.op) {
|
|
/* Need to perform the scatter read/modify/write at commit time */
|
|
if (env->vtcm_log.op_size == 2) {
|
|
SCATTER_OP_WRITE_TO_MEM(uint16_t);
|
|
} else if (env->vtcm_log.op_size == 4) {
|
|
/* Word Scatter += */
|
|
SCATTER_OP_WRITE_TO_MEM(uint32_t);
|
|
} else {
|
|
g_assert_not_reached();
|
|
}
|
|
} else {
|
|
for (int i = 0; i < sizeof(MMVector); i++) {
|
|
if (test_bit(i, env->vtcm_log.mask)) {
|
|
cpu_stb_data_ra(env, env->vtcm_log.va[i],
|
|
env->vtcm_log.data.ub[i], ra);
|
|
clear_bit(i, env->vtcm_log.mask);
|
|
env->vtcm_log.data.ub[i] = 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t HELPER(fcircadd)(int32_t RxV, int32_t offset, int32_t M, int32_t CS)
|
|
{
|
|
uint32_t K_const = extract32(M, 24, 4);
|
|
uint32_t length = extract32(M, 0, 17);
|
|
uint32_t new_ptr = RxV + offset;
|
|
uint32_t start_addr;
|
|
uint32_t end_addr;
|
|
|
|
if (K_const == 0 && length >= 4) {
|
|
start_addr = CS;
|
|
end_addr = start_addr + length;
|
|
} else {
|
|
/*
|
|
* Versions v3 and earlier used the K value to specify a power-of-2 size
|
|
* 2^(K+2) that is greater than the buffer length
|
|
*/
|
|
int32_t mask = (1 << (K_const + 2)) - 1;
|
|
start_addr = RxV & (~mask);
|
|
end_addr = start_addr | length;
|
|
}
|
|
|
|
if (new_ptr >= end_addr) {
|
|
new_ptr -= length;
|
|
} else if (new_ptr < start_addr) {
|
|
new_ptr += length;
|
|
}
|
|
|
|
return new_ptr;
|
|
}
|
|
|
|
uint32_t HELPER(fbrev)(uint32_t addr)
|
|
{
|
|
/*
|
|
* Bit reverse the low 16 bits of the address
|
|
*/
|
|
return deposit32(addr, 0, 16, revbit16(addr));
|
|
}
|
|
|
|
static float32 build_float32(uint8_t sign, uint32_t exp, uint32_t mant)
|
|
{
|
|
return make_float32(
|
|
((sign & 1) << 31) |
|
|
((exp & 0xff) << SF_MANTBITS) |
|
|
(mant & ((1 << SF_MANTBITS) - 1)));
|
|
}
|
|
|
|
/*
|
|
* sfrecipa, sfinvsqrta have two 32-bit results
|
|
* r0,p0=sfrecipa(r1,r2)
|
|
* r0,p0=sfinvsqrta(r1)
|
|
*
|
|
* Since helpers can only return a single value, we pack the two results
|
|
* into a 64-bit value.
|
|
*/
|
|
uint64_t HELPER(sfrecipa)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t PeV = 0;
|
|
float32 RdV;
|
|
int idx;
|
|
int adjust;
|
|
int mant;
|
|
int exp;
|
|
|
|
arch_fpop_start(env);
|
|
if (arch_sf_recip_common(&RsV, &RtV, &RdV, &adjust, &env->fp_status)) {
|
|
PeV = adjust;
|
|
idx = (RtV >> 16) & 0x7f;
|
|
mant = (recip_lookup_table[idx] << 15) | 1;
|
|
exp = SF_BIAS - (float32_getexp(RtV) - SF_BIAS) - 1;
|
|
RdV = build_float32(extract32(RtV, 31, 1), exp, mant);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return ((uint64_t)RdV << 32) | PeV;
|
|
}
|
|
|
|
uint64_t HELPER(sfinvsqrta)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int PeV = 0;
|
|
float32 RdV;
|
|
int idx;
|
|
int adjust;
|
|
int mant;
|
|
int exp;
|
|
|
|
arch_fpop_start(env);
|
|
if (arch_sf_invsqrt_common(&RsV, &RdV, &adjust, &env->fp_status)) {
|
|
PeV = adjust;
|
|
idx = (RsV >> 17) & 0x7f;
|
|
mant = (invsqrt_lookup_table[idx] << 15);
|
|
exp = SF_BIAS - ((float32_getexp(RsV) - SF_BIAS) >> 1) - 1;
|
|
RdV = build_float32(extract32(RsV, 31, 1), exp, mant);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return ((uint64_t)RdV << 32) | PeV;
|
|
}
|
|
|
|
int64_t HELPER(vacsh_val)(CPUHexagonState *env,
|
|
int64_t RxxV, int64_t RssV, int64_t RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
for (int i = 0; i < 4; i++) {
|
|
int xv = sextract64(RxxV, i * 16, 16);
|
|
int sv = sextract64(RssV, i * 16, 16);
|
|
int tv = sextract64(RttV, i * 16, 16);
|
|
int max;
|
|
xv = xv + tv;
|
|
sv = sv - tv;
|
|
max = xv > sv ? xv : sv;
|
|
/* Note that fSATH can set the OVF bit in usr */
|
|
RxxV = deposit64(RxxV, i * 16, 16, fSATH(max));
|
|
}
|
|
return RxxV;
|
|
}
|
|
|
|
int32_t HELPER(vacsh_pred)(CPUHexagonState *env,
|
|
int64_t RxxV, int64_t RssV, int64_t RttV)
|
|
{
|
|
int32_t PeV = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
int xv = sextract64(RxxV, i * 16, 16);
|
|
int sv = sextract64(RssV, i * 16, 16);
|
|
int tv = sextract64(RttV, i * 16, 16);
|
|
xv = xv + tv;
|
|
sv = sv - tv;
|
|
PeV = deposit32(PeV, i * 2, 1, (xv > sv));
|
|
PeV = deposit32(PeV, i * 2 + 1, 1, (xv > sv));
|
|
}
|
|
return PeV;
|
|
}
|
|
|
|
int64_t HELPER(cabacdecbin_val)(int64_t RssV, int64_t RttV)
|
|
{
|
|
int64_t RddV = 0;
|
|
size4u_t state;
|
|
size4u_t valMPS;
|
|
size4u_t bitpos;
|
|
size4u_t range;
|
|
size4u_t offset;
|
|
size4u_t rLPS;
|
|
size4u_t rMPS;
|
|
|
|
state = fEXTRACTU_RANGE(fGETWORD(1, RttV), 5, 0);
|
|
valMPS = fEXTRACTU_RANGE(fGETWORD(1, RttV), 8, 8);
|
|
bitpos = fEXTRACTU_RANGE(fGETWORD(0, RttV), 4, 0);
|
|
range = fGETWORD(0, RssV);
|
|
offset = fGETWORD(1, RssV);
|
|
|
|
/* calculate rLPS */
|
|
range <<= bitpos;
|
|
offset <<= bitpos;
|
|
rLPS = rLPS_table_64x4[state][(range >> 29) & 3];
|
|
rLPS = rLPS << 23; /* left aligned */
|
|
|
|
/* calculate rMPS */
|
|
rMPS = (range & 0xff800000) - rLPS;
|
|
|
|
/* most probable region */
|
|
if (offset < rMPS) {
|
|
RddV = AC_next_state_MPS_64[state];
|
|
fINSERT_RANGE(RddV, 8, 8, valMPS);
|
|
fINSERT_RANGE(RddV, 31, 23, (rMPS >> 23));
|
|
fSETWORD(1, RddV, offset);
|
|
}
|
|
/* least probable region */
|
|
else {
|
|
RddV = AC_next_state_LPS_64[state];
|
|
fINSERT_RANGE(RddV, 8, 8, ((!state) ? (1 - valMPS) : (valMPS)));
|
|
fINSERT_RANGE(RddV, 31, 23, (rLPS >> 23));
|
|
fSETWORD(1, RddV, (offset - rMPS));
|
|
}
|
|
return RddV;
|
|
}
|
|
|
|
int32_t HELPER(cabacdecbin_pred)(int64_t RssV, int64_t RttV)
|
|
{
|
|
int32_t p0 = 0;
|
|
size4u_t state;
|
|
size4u_t valMPS;
|
|
size4u_t bitpos;
|
|
size4u_t range;
|
|
size4u_t offset;
|
|
size4u_t rLPS;
|
|
size4u_t rMPS;
|
|
|
|
state = fEXTRACTU_RANGE(fGETWORD(1, RttV), 5, 0);
|
|
valMPS = fEXTRACTU_RANGE(fGETWORD(1, RttV), 8, 8);
|
|
bitpos = fEXTRACTU_RANGE(fGETWORD(0, RttV), 4, 0);
|
|
range = fGETWORD(0, RssV);
|
|
offset = fGETWORD(1, RssV);
|
|
|
|
/* calculate rLPS */
|
|
range <<= bitpos;
|
|
offset <<= bitpos;
|
|
rLPS = rLPS_table_64x4[state][(range >> 29) & 3];
|
|
rLPS = rLPS << 23; /* left aligned */
|
|
|
|
/* calculate rMPS */
|
|
rMPS = (range & 0xff800000) - rLPS;
|
|
|
|
/* most probable region */
|
|
if (offset < rMPS) {
|
|
p0 = valMPS;
|
|
|
|
}
|
|
/* least probable region */
|
|
else {
|
|
p0 = valMPS ^ 1;
|
|
}
|
|
return p0;
|
|
}
|
|
|
|
static void probe_store(CPUHexagonState *env, int slot, int mmu_idx,
|
|
bool is_predicated, uintptr_t retaddr)
|
|
{
|
|
if (!is_predicated || !(env->slot_cancelled & (1 << slot))) {
|
|
uint32_t width = env->mem_log_stores[slot].width;
|
|
target_ulong va = env->mem_log_stores[slot].va;
|
|
probe_write(env, va, width, mmu_idx, retaddr);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called from a mem_noshuf packet to make sure the load doesn't
|
|
* raise an exception
|
|
*/
|
|
void HELPER(probe_noshuf_load)(CPUHexagonState *env, target_ulong va,
|
|
int size, int mmu_idx)
|
|
{
|
|
uintptr_t retaddr = GETPC();
|
|
probe_read(env, va, size, mmu_idx, retaddr);
|
|
}
|
|
|
|
/* Called during packet commit when there are two scalar stores */
|
|
void HELPER(probe_pkt_scalar_store_s0)(CPUHexagonState *env, int args)
|
|
{
|
|
int mmu_idx = FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, MMU_IDX);
|
|
bool is_predicated =
|
|
FIELD_EX32(args, PROBE_PKT_SCALAR_STORE_S0, IS_PREDICATED);
|
|
uintptr_t ra = GETPC();
|
|
probe_store(env, 0, mmu_idx, is_predicated, ra);
|
|
}
|
|
|
|
static void probe_hvx_stores(CPUHexagonState *env, int mmu_idx,
|
|
uintptr_t retaddr)
|
|
{
|
|
/* Normal (possibly masked) vector store */
|
|
for (int i = 0; i < VSTORES_MAX; i++) {
|
|
if (env->vstore_pending[i]) {
|
|
target_ulong va = env->vstore[i].va;
|
|
int size = env->vstore[i].size;
|
|
for (int j = 0; j < size; j++) {
|
|
if (test_bit(j, env->vstore[i].mask)) {
|
|
probe_write(env, va + j, 1, mmu_idx, retaddr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Scatter store */
|
|
if (env->vtcm_pending) {
|
|
if (env->vtcm_log.op) {
|
|
/* Need to perform the scatter read/modify/write at commit time */
|
|
if (env->vtcm_log.op_size == 2) {
|
|
SCATTER_OP_PROBE_MEM(size2u_t, mmu_idx, retaddr);
|
|
} else if (env->vtcm_log.op_size == 4) {
|
|
/* Word Scatter += */
|
|
SCATTER_OP_PROBE_MEM(size4u_t, mmu_idx, retaddr);
|
|
} else {
|
|
g_assert_not_reached();
|
|
}
|
|
} else {
|
|
for (int i = 0; i < sizeof(MMVector); i++) {
|
|
if (test_bit(i, env->vtcm_log.mask)) {
|
|
probe_write(env, env->vtcm_log.va[i], 1, mmu_idx, retaddr);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HELPER(probe_hvx_stores)(CPUHexagonState *env, int mmu_idx)
|
|
{
|
|
uintptr_t retaddr = GETPC();
|
|
probe_hvx_stores(env, mmu_idx, retaddr);
|
|
}
|
|
|
|
void HELPER(probe_pkt_scalar_hvx_stores)(CPUHexagonState *env, int mask)
|
|
{
|
|
bool has_st0 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST0);
|
|
bool has_st1 = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_ST1);
|
|
bool has_hvx_stores =
|
|
FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, HAS_HVX_STORES);
|
|
bool s0_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S0_IS_PRED);
|
|
bool s1_is_pred = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, S1_IS_PRED);
|
|
int mmu_idx = FIELD_EX32(mask, PROBE_PKT_SCALAR_HVX_STORES, MMU_IDX);
|
|
uintptr_t ra = GETPC();
|
|
|
|
if (has_st0) {
|
|
probe_store(env, 0, mmu_idx, s0_is_pred, ra);
|
|
}
|
|
if (has_st1) {
|
|
probe_store(env, 1, mmu_idx, s1_is_pred, ra);
|
|
}
|
|
if (has_hvx_stores) {
|
|
probe_hvx_stores(env, mmu_idx, ra);
|
|
}
|
|
}
|
|
|
|
#ifndef CONFIG_HEXAGON_IDEF_PARSER
|
|
/*
|
|
* mem_noshuf
|
|
* Section 5.5 of the Hexagon V67 Programmer's Reference Manual
|
|
*
|
|
* If the load is in slot 0 and there is a store in slot1 (that
|
|
* wasn't cancelled), we have to do the store first.
|
|
*/
|
|
static void check_noshuf(CPUHexagonState *env, bool pkt_has_scalar_store_s1,
|
|
uint32_t slot, target_ulong vaddr, int size,
|
|
uintptr_t ra)
|
|
{
|
|
if (slot == 0 && pkt_has_scalar_store_s1 &&
|
|
((env->slot_cancelled & (1 << 1)) == 0)) {
|
|
probe_read(env, vaddr, size, MMU_USER_IDX, ra);
|
|
commit_store(env, 1, ra);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Floating point */
|
|
float64 HELPER(conv_sf2df)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 out_f64;
|
|
arch_fpop_start(env);
|
|
out_f64 = float32_to_float64(RsV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return out_f64;
|
|
}
|
|
|
|
float32 HELPER(conv_df2sf)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 out_f32;
|
|
arch_fpop_start(env);
|
|
out_f32 = float64_to_float32(RssV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return out_f32;
|
|
}
|
|
|
|
float32 HELPER(conv_uw2sf)(CPUHexagonState *env, int32_t RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = uint32_to_float32(RsV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float64 HELPER(conv_uw2df)(CPUHexagonState *env, int32_t RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = uint32_to_float64(RsV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float32 HELPER(conv_w2sf)(CPUHexagonState *env, int32_t RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = int32_to_float32(RsV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float64 HELPER(conv_w2df)(CPUHexagonState *env, int32_t RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = int32_to_float64(RsV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float32 HELPER(conv_ud2sf)(CPUHexagonState *env, int64_t RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = uint64_to_float32(RssV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float64 HELPER(conv_ud2df)(CPUHexagonState *env, int64_t RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = uint64_to_float64(RssV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float32 HELPER(conv_d2sf)(CPUHexagonState *env, int64_t RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = int64_to_float32(RssV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float64 HELPER(conv_d2df)(CPUHexagonState *env, int64_t RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = int64_to_float64(RssV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
uint32_t HELPER(conv_sf2uw)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = 0;
|
|
} else {
|
|
RdV = float32_to_uint32(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
int32_t HELPER(conv_sf2w)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float32_is_any_nan(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = -1;
|
|
} else {
|
|
RdV = float32_to_int32(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
uint64_t HELPER(conv_sf2ud)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = 0;
|
|
} else {
|
|
RddV = float32_to_uint64(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
int64_t HELPER(conv_sf2d)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float32_is_any_nan(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = -1;
|
|
} else {
|
|
RddV = float32_to_int64(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
uint32_t HELPER(conv_df2uw)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = 0;
|
|
} else {
|
|
RdV = float64_to_uint32(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
int32_t HELPER(conv_df2w)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float64_is_any_nan(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = -1;
|
|
} else {
|
|
RdV = float64_to_int32(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
uint64_t HELPER(conv_df2ud)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = 0;
|
|
} else {
|
|
RddV = float64_to_uint64(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
int64_t HELPER(conv_df2d)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float64_is_any_nan(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = -1;
|
|
} else {
|
|
RddV = float64_to_int64(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
uint32_t HELPER(conv_sf2uw_chop)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = 0;
|
|
} else {
|
|
RdV = float32_to_uint32_round_to_zero(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
int32_t HELPER(conv_sf2w_chop)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float32_is_any_nan(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = -1;
|
|
} else {
|
|
RdV = float32_to_int32_round_to_zero(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
uint64_t HELPER(conv_sf2ud_chop)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float32_is_neg(RsV) && !float32_is_any_nan(RsV) && !float32_is_zero(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = 0;
|
|
} else {
|
|
RddV = float32_to_uint64_round_to_zero(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
int64_t HELPER(conv_sf2d_chop)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float32_is_any_nan(RsV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = -1;
|
|
} else {
|
|
RddV = float32_to_int64_round_to_zero(RsV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
uint32_t HELPER(conv_df2uw_chop)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = 0;
|
|
} else {
|
|
RdV = float64_to_uint32_round_to_zero(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
int32_t HELPER(conv_df2w_chop)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t RdV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float64_is_any_nan(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RdV = -1;
|
|
} else {
|
|
RdV = float64_to_int32_round_to_zero(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
uint64_t HELPER(conv_df2ud_chop)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
uint64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon checks the sign before rounding */
|
|
if (float64_is_neg(RssV) && !float64_is_any_nan(RssV) && !float64_is_zero(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = 0;
|
|
} else {
|
|
RddV = float64_to_uint64_round_to_zero(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
int64_t HELPER(conv_df2d_chop)(CPUHexagonState *env, float64 RssV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int64_t RddV;
|
|
arch_fpop_start(env);
|
|
/* Hexagon returns -1 for NaN */
|
|
if (float64_is_any_nan(RssV)) {
|
|
float_raise(float_flag_invalid, &env->fp_status);
|
|
RddV = -1;
|
|
} else {
|
|
RddV = float64_to_int64_round_to_zero(RssV, &env->fp_status);
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float32 HELPER(sfadd)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = float32_add(RsV, RtV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float32 HELPER(sfsub)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = float32_sub(RsV, RtV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
int32_t HELPER(sfcmpeq)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
PdV = f8BITSOF(float32_eq_quiet(RsV, RtV, &env->fp_status));
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
int32_t HELPER(sfcmpgt)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int cmp;
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
cmp = float32_compare_quiet(RsV, RtV, &env->fp_status);
|
|
PdV = f8BITSOF(cmp == float_relation_greater);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
int32_t HELPER(sfcmpge)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int cmp;
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
cmp = float32_compare_quiet(RsV, RtV, &env->fp_status);
|
|
PdV = f8BITSOF(cmp == float_relation_greater ||
|
|
cmp == float_relation_equal);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
int32_t HELPER(sfcmpuo)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
PdV = f8BITSOF(float32_unordered_quiet(RsV, RtV, &env->fp_status));
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
float32 HELPER(sfmax)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = float32_maximum_number(RsV, RtV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float32 HELPER(sfmin)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = float32_minimum_number(RsV, RtV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
int32_t HELPER(sfclass)(CPUHexagonState *env, float32 RsV, int32_t uiV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t PdV = 0;
|
|
arch_fpop_start(env);
|
|
if (fGETBIT(0, uiV) && float32_is_zero(RsV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(1, uiV) && float32_is_normal(RsV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(2, uiV) && float32_is_denormal(RsV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(3, uiV) && float32_is_infinity(RsV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(4, uiV) && float32_is_any_nan(RsV)) {
|
|
PdV = 0xff;
|
|
}
|
|
set_float_exception_flags(0, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
float32 HELPER(sffixupn)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV = 0;
|
|
int adjust;
|
|
arch_fpop_start(env);
|
|
arch_sf_recip_common(&RsV, &RtV, &RdV, &adjust, &env->fp_status);
|
|
RdV = RsV;
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float32 HELPER(sffixupd)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV = 0;
|
|
int adjust;
|
|
arch_fpop_start(env);
|
|
arch_sf_recip_common(&RsV, &RtV, &RdV, &adjust, &env->fp_status);
|
|
RdV = RtV;
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float32 HELPER(sffixupr)(CPUHexagonState *env, float32 RsV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV = 0;
|
|
int adjust;
|
|
arch_fpop_start(env);
|
|
arch_sf_invsqrt_common(&RsV, &RdV, &adjust, &env->fp_status);
|
|
RdV = RsV;
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float64 HELPER(dfadd)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = float64_add(RssV, RttV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float64 HELPER(dfsub)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = float64_sub(RssV, RttV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float64 HELPER(dfmax)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = float64_maximum_number(RssV, RttV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float64 HELPER(dfmin)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float64 RddV;
|
|
arch_fpop_start(env);
|
|
RddV = float64_minimum_number(RssV, RttV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
int32_t HELPER(dfcmpeq)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
PdV = f8BITSOF(float64_eq_quiet(RssV, RttV, &env->fp_status));
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
int32_t HELPER(dfcmpgt)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int cmp;
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
cmp = float64_compare_quiet(RssV, RttV, &env->fp_status);
|
|
PdV = f8BITSOF(cmp == float_relation_greater);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
int32_t HELPER(dfcmpge)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int cmp;
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
cmp = float64_compare_quiet(RssV, RttV, &env->fp_status);
|
|
PdV = f8BITSOF(cmp == float_relation_greater ||
|
|
cmp == float_relation_equal);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
int32_t HELPER(dfcmpuo)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t PdV;
|
|
arch_fpop_start(env);
|
|
PdV = f8BITSOF(float64_unordered_quiet(RssV, RttV, &env->fp_status));
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
int32_t HELPER(dfclass)(CPUHexagonState *env, float64 RssV, int32_t uiV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int32_t PdV = 0;
|
|
arch_fpop_start(env);
|
|
if (fGETBIT(0, uiV) && float64_is_zero(RssV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(1, uiV) && float64_is_normal(RssV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(2, uiV) && float64_is_denormal(RssV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(3, uiV) && float64_is_infinity(RssV)) {
|
|
PdV = 0xff;
|
|
}
|
|
if (fGETBIT(4, uiV) && float64_is_any_nan(RssV)) {
|
|
PdV = 0xff;
|
|
}
|
|
set_float_exception_flags(0, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return PdV;
|
|
}
|
|
|
|
float32 HELPER(sfmpy)(CPUHexagonState *env, float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
float32 RdV;
|
|
arch_fpop_start(env);
|
|
RdV = float32_mul(RsV, RtV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RdV;
|
|
}
|
|
|
|
float32 HELPER(sffma)(CPUHexagonState *env, float32 RxV,
|
|
float32 RsV, float32 RtV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
arch_fpop_start(env);
|
|
RxV = float32_muladd(RsV, RtV, RxV, 0, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RxV;
|
|
}
|
|
|
|
float32 HELPER(sffma_sc)(CPUHexagonState *env, float32 RxV,
|
|
float32 RsV, float32 RtV, float32 PuV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
arch_fpop_start(env);
|
|
RxV = float32_muladd_scalbn(RsV, RtV, RxV, fSXTN(8, 64, PuV),
|
|
float_muladd_suppress_add_product_zero,
|
|
&env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RxV;
|
|
}
|
|
|
|
float32 HELPER(sffms)(CPUHexagonState *env, float32 RxV,
|
|
float32 RsV, float32 RtV, uint32_t pkt_need_commit)
|
|
{
|
|
arch_fpop_start(env);
|
|
RxV = float32_muladd(RsV, RtV, RxV, float_muladd_negate_product,
|
|
&env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RxV;
|
|
}
|
|
|
|
static float32 do_sffma_lib(CPUHexagonState *env, float32 RxV,
|
|
float32 RsV, float32 RtV, int negate,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int flags;
|
|
|
|
arch_fpop_start(env);
|
|
|
|
set_float_rounding_mode(float_round_nearest_even_max, &env->fp_status);
|
|
RxV = float32_muladd(RsV, RtV, RxV,
|
|
negate | float_muladd_suppress_add_product_zero,
|
|
&env->fp_status);
|
|
|
|
flags = get_float_exception_flags(&env->fp_status);
|
|
if (flags) {
|
|
/* Flags are suppressed by this instruction. */
|
|
set_float_exception_flags(0, &env->fp_status);
|
|
|
|
/* Return 0 for Inf - Inf. */
|
|
if (flags & float_flag_invalid_isi) {
|
|
RxV = 0;
|
|
}
|
|
}
|
|
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RxV;
|
|
}
|
|
|
|
float32 HELPER(sffma_lib)(CPUHexagonState *env, float32 RxV,
|
|
float32 RsV, float32 RtV, uint32_t pkt_need_commit)
|
|
{
|
|
return do_sffma_lib(env, RxV, RsV, RtV, 0, pkt_need_commit);
|
|
}
|
|
|
|
float32 HELPER(sffms_lib)(CPUHexagonState *env, float32 RxV,
|
|
float32 RsV, float32 RtV, uint32_t pkt_need_commit)
|
|
{
|
|
return do_sffma_lib(env, RxV, RsV, RtV, float_muladd_negate_product,
|
|
pkt_need_commit);
|
|
}
|
|
|
|
float64 HELPER(dfmpyfix)(CPUHexagonState *env, float64 RssV, float64 RttV,
|
|
uint32_t pkt_need_commit)
|
|
{
|
|
int64_t RddV;
|
|
arch_fpop_start(env);
|
|
if (float64_is_denormal(RssV) &&
|
|
(float64_getexp(RttV) >= 512) &&
|
|
float64_is_normal(RttV)) {
|
|
RddV = float64_mul(RssV, make_float64(0x4330000000000000),
|
|
&env->fp_status);
|
|
} else if (float64_is_denormal(RttV) &&
|
|
(float64_getexp(RssV) >= 512) &&
|
|
float64_is_normal(RssV)) {
|
|
RddV = float64_mul(RssV, make_float64(0x3cb0000000000000),
|
|
&env->fp_status);
|
|
} else {
|
|
RddV = RssV;
|
|
}
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RddV;
|
|
}
|
|
|
|
float64 HELPER(dfmpyhh)(CPUHexagonState *env, float64 RxxV,
|
|
float64 RssV, float64 RttV, uint32_t pkt_need_commit)
|
|
{
|
|
arch_fpop_start(env);
|
|
RxxV = internal_mpyhh(RssV, RttV, RxxV, &env->fp_status);
|
|
arch_fpop_end(env, pkt_need_commit);
|
|
return RxxV;
|
|
}
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
void HELPER(modify_ssr)(CPUHexagonState *env, uint32_t new, uint32_t old)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
hexagon_modify_ssr(env, new, old);
|
|
}
|
|
|
|
static void hex_k0_lock(CPUHexagonState *env)
|
|
{
|
|
HexagonCPU *cpu = env_archcpu(env);
|
|
CPUState *cs = env_cpu(env);
|
|
target_ulong syscfg;
|
|
|
|
BQL_LOCK_GUARD();
|
|
g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1));
|
|
|
|
syscfg = cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
|
|
env->threadId) : 0;
|
|
if (GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg)) {
|
|
if (env->k0_lock_state == HEX_LOCK_QUEUED) {
|
|
env->next_PC += 4;
|
|
env->k0_lock_count++;
|
|
env->k0_lock_state = HEX_LOCK_OWNER;
|
|
SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1);
|
|
return;
|
|
}
|
|
if (env->k0_lock_state == HEX_LOCK_OWNER) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"Double k0lock at PC: 0x%" PRIx32
|
|
", thread may hang\n",
|
|
env->next_PC);
|
|
env->next_PC += 4;
|
|
cpu_interrupt(cs, CPU_INTERRUPT_HALT);
|
|
cpu_loop_exit(cs);
|
|
return;
|
|
}
|
|
env->k0_lock_state = HEX_LOCK_WAITING;
|
|
cpu_interrupt(cs, CPU_INTERRUPT_HALT);
|
|
cpu_loop_exit(cs);
|
|
} else {
|
|
env->next_PC += 4;
|
|
env->k0_lock_count++;
|
|
env->k0_lock_state = HEX_LOCK_OWNER;
|
|
SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 1);
|
|
}
|
|
}
|
|
|
|
static void hex_k0_unlock(CPUHexagonState *env)
|
|
{
|
|
HexagonCPU *cpu = env_archcpu(env);
|
|
unsigned int this_threadId = env->threadId;
|
|
CPUHexagonState *unlock_thread = NULL;
|
|
CPUState *cs;
|
|
target_ulong syscfg;
|
|
|
|
BQL_LOCK_GUARD();
|
|
g_assert((env->k0_lock_count == 0) || (env->k0_lock_count == 1));
|
|
|
|
/* Nothing to do if the k0 isn't locked by this thread */
|
|
syscfg = cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SYSCFG,
|
|
env->threadId) : 0;
|
|
if ((GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg) == 0) ||
|
|
(env->k0_lock_state != HEX_LOCK_OWNER)) {
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"thread %" PRIu32 " attempted to unlock k0 without"
|
|
" having the lock, k0_lock state = %u,"
|
|
" syscfg:k0 = %" PRIu32 "\n",
|
|
env->threadId, (unsigned)env->k0_lock_state,
|
|
(uint32_t)GET_SYSCFG_FIELD(SYSCFG_K0LOCK, syscfg));
|
|
g_assert(env->k0_lock_state != HEX_LOCK_WAITING);
|
|
return;
|
|
}
|
|
|
|
env->k0_lock_count--;
|
|
env->k0_lock_state = HEX_LOCK_UNLOCKED;
|
|
SET_SYSCFG_FIELD(env, SYSCFG_K0LOCK, 0);
|
|
|
|
/* Look for a thread to unlock */
|
|
CPU_FOREACH(cs) {
|
|
CPUHexagonState *thread = cpu_env(cs);
|
|
|
|
/*
|
|
* The hardware implements round-robin fairness, so we look for threads
|
|
* starting at env->threadId + 1 and incrementing modulo the number of
|
|
* threads.
|
|
*
|
|
* To implement this, we check if thread is a earlier in the modulo
|
|
* sequence than unlock_thread.
|
|
* if unlock thread is higher than this thread
|
|
* thread must be between this thread and unlock_thread
|
|
* else
|
|
* thread higher than this thread is ahead of unlock_thread
|
|
* thread must be lower then unlock thread
|
|
*/
|
|
if (thread->k0_lock_state == HEX_LOCK_WAITING) {
|
|
if (!unlock_thread) {
|
|
unlock_thread = thread;
|
|
} else if (unlock_thread->threadId > this_threadId) {
|
|
if (this_threadId < thread->threadId &&
|
|
thread->threadId < unlock_thread->threadId) {
|
|
unlock_thread = thread;
|
|
}
|
|
} else {
|
|
if (thread->threadId > this_threadId) {
|
|
unlock_thread = thread;
|
|
}
|
|
if (thread->threadId < unlock_thread->threadId) {
|
|
unlock_thread = thread;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (unlock_thread) {
|
|
cs = env_cpu(unlock_thread);
|
|
unlock_thread->k0_lock_state = HEX_LOCK_QUEUED;
|
|
SET_SYSCFG_FIELD(unlock_thread, SYSCFG_K0LOCK, 1);
|
|
cpu_interrupt(cs, CPU_INTERRUPT_K0_UNLOCK);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
/* Histogram instructions */
|
|
|
|
void HELPER(vhist)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int lane = 0; lane < 8; lane++) {
|
|
for (int i = 0; i < sizeof(MMVector) / 8; ++i) {
|
|
unsigned char value = input->ub[(sizeof(MMVector) / 8) * lane + i];
|
|
unsigned char regno = value >> 3;
|
|
unsigned char element = value & 7;
|
|
|
|
env->VRegs[regno].uh[(sizeof(MMVector) / 16) * lane + element]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HELPER(vhistq)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int lane = 0; lane < 8; lane++) {
|
|
for (int i = 0; i < sizeof(MMVector) / 8; ++i) {
|
|
unsigned char value = input->ub[(sizeof(MMVector) / 8) * lane + i];
|
|
unsigned char regno = value >> 3;
|
|
unsigned char element = value & 7;
|
|
|
|
if (fGETQBIT(env->qtmp, sizeof(MMVector) / 8 * lane + i)) {
|
|
env->VRegs[regno].uh[
|
|
(sizeof(MMVector) / 16) * lane + element]++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist256)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7);
|
|
|
|
env->VRegs[vindex].uh[elindex] =
|
|
env->VRegs[vindex].uh[elindex] + weight;
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist256q)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7);
|
|
|
|
if (fGETQBIT(env->qtmp, 2 * i)) {
|
|
env->VRegs[vindex].uh[elindex] =
|
|
env->VRegs[vindex].uh[elindex] + weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist256_sat)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7);
|
|
|
|
env->VRegs[vindex].uh[elindex] =
|
|
fVSATUH(env->VRegs[vindex].uh[elindex] + weight);
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist256q_sat)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 0) & (~7)) | ((bucket >> 0) & 7);
|
|
|
|
if (fGETQBIT(env->qtmp, 2 * i)) {
|
|
env->VRegs[vindex].uh[elindex] =
|
|
fVSATUH(env->VRegs[vindex].uh[elindex] + weight);
|
|
}
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist128)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3);
|
|
|
|
env->VRegs[vindex].uw[elindex] =
|
|
env->VRegs[vindex].uw[elindex] + weight;
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist128q)(CPUHexagonState *env)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3);
|
|
|
|
if (fGETQBIT(env->qtmp, 2 * i)) {
|
|
env->VRegs[vindex].uw[elindex] =
|
|
env->VRegs[vindex].uw[elindex] + weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist128m)(CPUHexagonState *env, int32_t uiV)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3);
|
|
|
|
if ((bucket & 1) == uiV) {
|
|
env->VRegs[vindex].uw[elindex] =
|
|
env->VRegs[vindex].uw[elindex] + weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
void HELPER(vwhist128qm)(CPUHexagonState *env, int32_t uiV)
|
|
{
|
|
MMVector *input = &env->tmp_VRegs[0];
|
|
|
|
for (int i = 0; i < (sizeof(MMVector) / 2); i++) {
|
|
unsigned int bucket = fGETUBYTE(0, input->h[i]);
|
|
unsigned int weight = fGETUBYTE(1, input->h[i]);
|
|
unsigned int vindex = (bucket >> 3) & 0x1F;
|
|
unsigned int elindex = ((i >> 1) & (~3)) | ((bucket >> 1) & 3);
|
|
|
|
if (((bucket & 1) == uiV) && fGETQBIT(env->qtmp, 2 * i)) {
|
|
env->VRegs[vindex].uw[elindex] =
|
|
env->VRegs[vindex].uw[elindex] + weight;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
void HELPER(raise_stack_overflow)(CPUHexagonState *env, uint32_t slot,
|
|
uint32_t badva)
|
|
{
|
|
/*
|
|
* Per section 7.3.1 of the V67 Programmer's Reference,
|
|
* stack limit exception isn't raised in monitor mode.
|
|
*/
|
|
uint32_t ssr = env->t_sreg[HEX_SREG_SSR];
|
|
CPUState *cs;
|
|
|
|
if (GET_SSR_FIELD(SSR_EX, ssr) ||
|
|
!GET_SSR_FIELD(SSR_UM, ssr)) {
|
|
return;
|
|
}
|
|
|
|
cs = env_cpu(env);
|
|
cs->exception_index = HEX_EVENT_PRECISE;
|
|
env->cause_code = HEX_CAUSE_STACK_LIMIT;
|
|
ASSERT_DIRECT_TO_GUEST_UNSET(env, cs->exception_index);
|
|
|
|
if (slot == 0) {
|
|
env->t_sreg[HEX_SREG_BADVA0] = badva;
|
|
SET_SSR_FIELD(env, SSR_V0, 1);
|
|
SET_SSR_FIELD(env, SSR_V1, 0);
|
|
SET_SSR_FIELD(env, SSR_BVS, 0);
|
|
} else if (slot == 1) {
|
|
env->t_sreg[HEX_SREG_BADVA1] = badva;
|
|
SET_SSR_FIELD(env, SSR_V0, 0);
|
|
SET_SSR_FIELD(env, SSR_V1, 1);
|
|
SET_SSR_FIELD(env, SSR_BVS, 1);
|
|
} else {
|
|
g_assert_not_reached();
|
|
}
|
|
cpu_loop_exit_restore(cs, 0);
|
|
}
|
|
|
|
void HELPER(ciad)(CPUHexagonState *env, uint32_t mask)
|
|
{
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
void HELPER(siad)(CPUHexagonState *env, uint32_t mask)
|
|
{
|
|
uint32_t ipendad;
|
|
uint32_t iad;
|
|
HexagonCPU *cpu;
|
|
|
|
BQL_LOCK_GUARD();
|
|
cpu = env_archcpu(env);
|
|
ipendad = cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_IPENDAD,
|
|
env->threadId) : 0;
|
|
iad = fGET_FIELD(ipendad, IPENDAD_IAD);
|
|
fSET_FIELD(ipendad, IPENDAD_IAD, iad | mask);
|
|
if (cpu->globalregs) {
|
|
hexagon_globalreg_write(cpu->globalregs, HEX_SREG_IPENDAD,
|
|
ipendad, env->threadId);
|
|
}
|
|
hex_interrupt_update(env);
|
|
}
|
|
|
|
void HELPER(swi)(CPUHexagonState *env, uint32_t mask)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
hex_raise_interrupts(env, mask, CPU_INTERRUPT_SWI);
|
|
}
|
|
|
|
void HELPER(cswi)(CPUHexagonState *env, uint32_t mask)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
hex_clear_interrupts(env, mask, CPU_INTERRUPT_SWI);
|
|
}
|
|
|
|
void HELPER(iassignw)(CPUHexagonState *env, uint32_t src)
|
|
{
|
|
uint32_t modectl;
|
|
uint32_t thread_enabled_mask;
|
|
CPUState *cpu;
|
|
HexagonCPU *hex_cpu;
|
|
|
|
BQL_LOCK_GUARD();
|
|
hex_cpu = env_archcpu(env);
|
|
modectl = hex_cpu->globalregs ?
|
|
hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL,
|
|
env->threadId) : 0;
|
|
thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
|
|
|
|
CPU_FOREACH(cpu) {
|
|
CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env);
|
|
uint32_t thread_id_mask = 0x1 << thread_env->threadId;
|
|
if (thread_enabled_mask & thread_id_mask) {
|
|
uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK];
|
|
uint32_t intbitpos = (src >> 16) & 0xF;
|
|
uint32_t val = (src >> thread_env->threadId) & 0x1;
|
|
imask = deposit32(imask, intbitpos, 1, val);
|
|
thread_env->t_sreg[HEX_SREG_IMASK] = imask;
|
|
|
|
qemu_log_mask(CPU_LOG_INT, "%s: thread " TARGET_FMT_ld
|
|
", new imask 0x%" PRIx32 "\n", __func__,
|
|
thread_env->threadId, imask);
|
|
}
|
|
}
|
|
hex_interrupt_update(env);
|
|
}
|
|
|
|
uint32_t HELPER(iassignr)(CPUHexagonState *env, uint32_t src)
|
|
{
|
|
uint32_t modectl;
|
|
uint32_t thread_enabled_mask;
|
|
uint32_t intbitpos;
|
|
uint32_t dest_reg;
|
|
CPUState *cpu;
|
|
HexagonCPU *hex_cpu;
|
|
|
|
BQL_LOCK_GUARD();
|
|
hex_cpu = env_archcpu(env);
|
|
modectl = hex_cpu->globalregs ?
|
|
hexagon_globalreg_read(hex_cpu->globalregs, HEX_SREG_MODECTL,
|
|
env->threadId) : 0;
|
|
thread_enabled_mask = GET_FIELD(MODECTL_E, modectl);
|
|
/* src fields are in same position as modectl, but mean different things */
|
|
intbitpos = GET_FIELD(MODECTL_W, src);
|
|
dest_reg = 0;
|
|
CPU_FOREACH(cpu) {
|
|
CPUHexagonState *thread_env = &(HEXAGON_CPU(cpu)->env);
|
|
uint32_t thread_id_mask = 0x1 << thread_env->threadId;
|
|
if (thread_enabled_mask & thread_id_mask) {
|
|
uint32_t imask = thread_env->t_sreg[HEX_SREG_IMASK];
|
|
dest_reg |= ((imask >> intbitpos) & 0x1) << thread_env->threadId;
|
|
}
|
|
}
|
|
|
|
return dest_reg;
|
|
}
|
|
|
|
void HELPER(start)(CPUHexagonState *env, uint32_t imask)
|
|
{
|
|
hexagon_start_threads(env, imask);
|
|
}
|
|
|
|
void HELPER(stop)(CPUHexagonState *env)
|
|
{
|
|
hexagon_stop_thread(env);
|
|
}
|
|
|
|
static void set_wait_mode(CPUHexagonState *env)
|
|
{
|
|
HexagonCPU *cpu;
|
|
uint32_t modectl;
|
|
uint32_t thread_wait_mask;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
cpu = env_archcpu(env);
|
|
if (!cpu->globalregs) {
|
|
return;
|
|
}
|
|
modectl =
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_MODECTL,
|
|
env->threadId);
|
|
thread_wait_mask = GET_FIELD(MODECTL_W, modectl);
|
|
thread_wait_mask |= 0x1 << env->threadId;
|
|
SET_SYSTEM_FIELD(env, HEX_SREG_MODECTL, MODECTL_W, thread_wait_mask);
|
|
}
|
|
|
|
static void hexagon_wait_thread(CPUHexagonState *env, uint32_t PC)
|
|
{
|
|
CPUState *cs;
|
|
|
|
g_assert(bql_locked());
|
|
|
|
if (qemu_loglevel_mask(LOG_GUEST_ERROR) &&
|
|
(env->k0_lock_state != HEX_LOCK_UNLOCKED ||
|
|
env->tlb_lock_state != HEX_LOCK_UNLOCKED)) {
|
|
qemu_log("WARNING: executing wait() with acquired lock"
|
|
"may lead to deadlock\n");
|
|
}
|
|
g_assert(get_exe_mode(env) != HEX_EXE_MODE_WAIT);
|
|
|
|
cs = env_cpu(env);
|
|
/*
|
|
* The addtion of cpu_has_work is borrowed from arm's wfi helper
|
|
* and is critical for our stability
|
|
*/
|
|
if ((cs->exception_index != HEX_EVENT_NONE) ||
|
|
(cpu_has_work(cs))) {
|
|
qemu_log_mask(CPU_LOG_INT,
|
|
"%s: thread %" PRIu32 " skipping WAIT mode, have some work\n",
|
|
__func__, env->threadId);
|
|
return;
|
|
}
|
|
set_wait_mode(env);
|
|
env->wait_next_pc = PC + 4;
|
|
|
|
cpu_interrupt(cs, CPU_INTERRUPT_HALT);
|
|
}
|
|
|
|
static inline QEMU_ALWAYS_INLINE void resched(CPUHexagonState *env)
|
|
{
|
|
uint32_t schedcfg;
|
|
uint32_t schedcfg_en;
|
|
int int_number;
|
|
CPUState *cs;
|
|
uint32_t lowest_th_prio = 0; /* 0 is highest prio */
|
|
uint32_t bestwait_reg;
|
|
uint32_t best_prio;
|
|
HexagonCPU *cpu;
|
|
|
|
BQL_LOCK_GUARD();
|
|
qemu_log_mask(CPU_LOG_INT, "%s: check resched\n", __func__);
|
|
cpu = env_archcpu(env);
|
|
schedcfg = cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_SCHEDCFG,
|
|
env->threadId) : 0;
|
|
schedcfg_en = GET_FIELD(SCHEDCFG_EN, schedcfg);
|
|
int_number = GET_FIELD(SCHEDCFG_INTNO, schedcfg);
|
|
|
|
if (!schedcfg_en) {
|
|
return;
|
|
}
|
|
|
|
CPU_FOREACH(cs) {
|
|
HexagonCPU *thread = HEXAGON_CPU(cs);
|
|
CPUHexagonState *thread_env = &(thread->env);
|
|
uint32_t th_prio = GET_FIELD(
|
|
STID_PRIO, thread_env->t_sreg[HEX_SREG_STID]);
|
|
if (!hexagon_thread_is_enabled(thread_env)) {
|
|
continue;
|
|
}
|
|
|
|
lowest_th_prio = (lowest_th_prio > th_prio)
|
|
? lowest_th_prio
|
|
: th_prio;
|
|
}
|
|
|
|
bestwait_reg = cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, HEX_SREG_BESTWAIT,
|
|
env->threadId) : 0;
|
|
best_prio = GET_FIELD(BESTWAIT_PRIO, bestwait_reg);
|
|
|
|
/*
|
|
* If the lowest priority thread is lower priority than the
|
|
* value in the BESTWAIT register, we must raise the reschedule
|
|
* interrupt on the lowest priority thread.
|
|
*/
|
|
if (lowest_th_prio > best_prio) {
|
|
qemu_log_mask(CPU_LOG_INT,
|
|
"%s: raising resched int %u,"
|
|
" cur PC 0x%" PRIx32 "\n",
|
|
__func__, (unsigned)int_number, env->gpr[HEX_REG_PC]);
|
|
SET_SYSTEM_FIELD(env, HEX_SREG_BESTWAIT, BESTWAIT_PRIO, ~0);
|
|
hex_raise_interrupts(env, 1 << int_number, CPU_INTERRUPT_SWI);
|
|
}
|
|
}
|
|
|
|
void HELPER(resched)(CPUHexagonState *env)
|
|
{
|
|
resched(env);
|
|
}
|
|
|
|
void HELPER(wait)(CPUHexagonState *env, uint32_t PC)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
|
|
if (!fIN_DEBUG_MODE(env->threadId)) {
|
|
hexagon_wait_thread(env, PC);
|
|
}
|
|
}
|
|
|
|
void HELPER(resume)(CPUHexagonState *env, uint32_t mask)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
hexagon_resume_threads(env, mask);
|
|
}
|
|
|
|
uint32_t HELPER(getimask)(CPUHexagonState *env, uint32_t tid)
|
|
{
|
|
CPUState *cs;
|
|
BQL_LOCK_GUARD();
|
|
CPU_FOREACH(cs) {
|
|
HexagonCPU *found_cpu = HEXAGON_CPU(cs);
|
|
CPUHexagonState *found_env = &found_cpu->env;
|
|
if (found_env->threadId == tid) {
|
|
uint32_t imask = found_env->t_sreg[HEX_SREG_IMASK];
|
|
qemu_log_mask(CPU_LOG_INT, "%s: tid " TARGET_FMT_lx
|
|
" imask = 0x%" PRIx32 "\n", __func__,
|
|
env->threadId,
|
|
(uint32_t)GET_FIELD(IMASK_MASK, imask));
|
|
return GET_FIELD(IMASK_MASK, imask);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void HELPER(setimask)(CPUHexagonState *env, uint32_t tid, uint32_t imask)
|
|
{
|
|
CPUState *cs;
|
|
|
|
BQL_LOCK_GUARD();
|
|
CPU_FOREACH(cs) {
|
|
HexagonCPU *found_cpu = HEXAGON_CPU(cs);
|
|
CPUHexagonState *found_env = &found_cpu->env;
|
|
|
|
if (tid == found_env->threadId) {
|
|
SET_SYSTEM_FIELD(found_env, HEX_SREG_IMASK, IMASK_MASK, imask);
|
|
qemu_log_mask(CPU_LOG_INT, "%s: tid " TARGET_FMT_lx
|
|
" imask 0x%" PRIx32 "\n",
|
|
__func__, found_env->threadId, imask);
|
|
hex_interrupt_update(found_env);
|
|
return;
|
|
}
|
|
}
|
|
qemu_log_mask(LOG_GUEST_ERROR,
|
|
"setimask used with an invalid tid near PC: 0x%"
|
|
PRIx32 "\n", env->next_PC);
|
|
}
|
|
|
|
void HELPER(sreg_write_masked)(CPUHexagonState *env, uint32_t reg, uint32_t val)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
if (reg < HEX_SREG_GLB_START) {
|
|
env->t_sreg[reg] = val;
|
|
} else {
|
|
HexagonCPU *cpu = env_archcpu(env);
|
|
if (cpu->globalregs) {
|
|
hexagon_globalreg_write_masked(cpu->globalregs, reg, val);
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline QEMU_ALWAYS_INLINE uint32_t sreg_read(CPUHexagonState *env,
|
|
uint32_t reg)
|
|
{
|
|
HexagonCPU *cpu;
|
|
|
|
g_assert(bql_locked());
|
|
if (reg < HEX_SREG_GLB_START) {
|
|
return env->t_sreg[reg];
|
|
}
|
|
cpu = env_archcpu(env);
|
|
return cpu->globalregs ?
|
|
hexagon_globalreg_read(cpu->globalregs, reg, env->threadId) : 0;
|
|
}
|
|
|
|
uint32_t HELPER(sreg_read)(CPUHexagonState *env, uint32_t reg)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
return sreg_read(env, reg);
|
|
}
|
|
|
|
uint64_t HELPER(sreg_read_pair)(CPUHexagonState *env, uint32_t reg)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
|
|
return deposit64((uint64_t) sreg_read(env, reg), 32, 32,
|
|
sreg_read(env, reg + 1));
|
|
}
|
|
|
|
uint32_t HELPER(greg_read)(CPUHexagonState *env, uint32_t reg)
|
|
|
|
{
|
|
return hexagon_greg_read(env, reg);
|
|
}
|
|
|
|
uint64_t HELPER(greg_read_pair)(CPUHexagonState *env, uint32_t reg)
|
|
|
|
{
|
|
if (reg == HEX_GREG_G0 || reg == HEX_GREG_G2) {
|
|
return (uint64_t)(env->greg[reg]) |
|
|
(((uint64_t)(env->greg[reg + 1])) << 32);
|
|
}
|
|
switch (reg) {
|
|
case HEX_GREG_GPCYCLELO:
|
|
return hexagon_get_sys_pcycle_count(env);
|
|
default:
|
|
return (uint64_t)hexagon_greg_read(env, reg) |
|
|
((uint64_t)(hexagon_greg_read(env, reg + 1)) << 32);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* setprio/resched - hardware-assisted scheduler helpers for managing
|
|
* the run queue and interrupt steering.
|
|
*/
|
|
void HELPER(setprio)(CPUHexagonState *env, uint32_t thread, uint32_t prio)
|
|
{
|
|
CPUState *cs;
|
|
|
|
BQL_LOCK_GUARD();
|
|
CPU_FOREACH(cs) {
|
|
HexagonCPU *found_cpu = HEXAGON_CPU(cs);
|
|
CPUHexagonState *found_env = &found_cpu->env;
|
|
if (thread == found_env->threadId) {
|
|
SET_SYSTEM_FIELD(found_env, HEX_SREG_STID, STID_PRIO, prio);
|
|
qemu_log_mask(CPU_LOG_INT,
|
|
"%s: tid %" PRIu32 " prio = 0x%" PRIx32 "\n",
|
|
__func__, found_env->threadId, prio);
|
|
resched(env);
|
|
return;
|
|
}
|
|
}
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
|
|
void HELPER(pending_interrupt)(CPUHexagonState *env)
|
|
{
|
|
BQL_LOCK_GUARD();
|
|
hex_interrupt_update(env);
|
|
}
|
|
#endif
|
|
|
|
|
|
/* These macros can be referenced in the generated helper functions */
|
|
#define warn(...) /* Nothing */
|
|
#define fatal(...) g_assert_not_reached();
|
|
|
|
#define BOGUS_HELPER(tag) \
|
|
printf("ERROR: bogus helper: " #tag "\n")
|
|
|
|
#include "helper_funcs_generated.c.inc"
|