aboutsummaryrefslogtreecommitdiff
path: root/target/arm/vfp_fpscr.c
blob: 92ea60ebbf28df0bc4b43fc2f3ab939f48750783 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * ARM VFP floating-point: handling of FPSCR/FPCR/FPSR
 *
 *  Copyright (c) 2003 Fabrice Bellard
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

#include "qemu/osdep.h"
#include "cpu.h"
#include "internals.h"
#include "cpu-features.h"

uint32_t vfp_get_fpcr(CPUARMState *env)
{
    uint32_t fpcr = env->vfp.fpcr
        | (env->vfp.vec_len << 16)
        | (env->vfp.vec_stride << 20);

    /*
     * M-profile LTPSIZE is the same bits [18:16] as A-profile Len; whichever
     * of the two is not applicable to this CPU will always be zero.
     */
    fpcr |= env->v7m.ltpsize << 16;

    return fpcr;
}

uint32_t vfp_get_fpsr(CPUARMState *env)
{
    uint32_t fpsr = env->vfp.fpsr;
    uint32_t i;

    fpsr |= vfp_get_fpsr_from_host(env);

    i = env->vfp.qc[0] | env->vfp.qc[1] | env->vfp.qc[2] | env->vfp.qc[3];
    fpsr |= i ? FPSR_QC : 0;
    return fpsr;
}

uint32_t vfp_get_fpscr(CPUARMState *env)
{
    return (vfp_get_fpcr(env) & FPSCR_FPCR_MASK) |
        (vfp_get_fpsr(env) & FPSCR_FPSR_MASK);
}

void vfp_set_fpsr(CPUARMState *env, uint32_t val)
{
    ARMCPU *cpu = env_archcpu(env);

    if (arm_feature(env, ARM_FEATURE_NEON) ||
        cpu_isar_feature(aa32_mve, cpu)) {
        /*
         * The bit we set within vfp.qc[] is arbitrary; the array as a
         * whole being zero/non-zero is what counts.
         */
        env->vfp.qc[0] = val & FPSR_QC;
        env->vfp.qc[1] = 0;
        env->vfp.qc[2] = 0;
        env->vfp.qc[3] = 0;
    }

    /*
     * NZCV lives only in env->vfp.fpsr. The cumulative exception flags
     * IOC|DZC|OFC|UFC|IXC|IDC also live in env->vfp.fpsr, with possible
     * extra pending exception information that hasn't yet been folded in
     * living in the float_status values (for TCG).
     * Since this FPSR write gives us the up to date values of the exception
     * flags, we want to store into vfp.fpsr the NZCV and CEXC bits, zeroing
     * anything else. We also need to clear out the float_status exception
     * information so that the next vfp_get_fpsr does not fold in stale data.
     */
    val &= FPSR_NZCV_MASK | FPSR_CEXC_MASK;
    env->vfp.fpsr = val;
    vfp_clear_float_status_exc_flags(env);
}

static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask)
{
    /*
     * We only set FPCR bits defined by mask, and leave the others alone.
     * We assume the mask is sensible (e.g. doesn't try to set only
     * part of a field)
     */
    ARMCPU *cpu = env_archcpu(env);

    /* When ARMv8.2-FP16 is not supported, FZ16 is RES0.  */
    if (!cpu_isar_feature(any_fp16, cpu)) {
        val &= ~FPCR_FZ16;
    }
    if (!cpu_isar_feature(aa64_afp, cpu)) {
        val &= ~(FPCR_FIZ | FPCR_AH | FPCR_NEP);
    }

    if (!cpu_isar_feature(aa64_ebf16, cpu)) {
        val &= ~FPCR_EBF;
    }

    vfp_set_fpcr_to_host(env, val, mask);

    if (mask & (FPCR_LEN_MASK | FPCR_STRIDE_MASK)) {
        if (!arm_feature(env, ARM_FEATURE_M)) {
            /*
             * Short-vector length and stride; on M-profile these bits
             * are used for different purposes.
             * We can't make this conditional be "if MVFR0.FPShVec != 0",
             * because in v7A no-short-vector-support cores still had to
             * allow Stride/Len to be written with the only effect that
             * some insns are required to UNDEF if the guest sets them.
             */
            env->vfp.vec_len = extract32(val, 16, 3);
            env->vfp.vec_stride = extract32(val, 20, 2);
        } else if (cpu_isar_feature(aa32_mve, cpu)) {
            env->v7m.ltpsize = extract32(val, FPCR_LTPSIZE_SHIFT,
                                         FPCR_LTPSIZE_LENGTH);
        }
    }

    /*
     * We don't implement trapped exception handling, so the
     * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!)
     *
     * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF, FZ16,
     * FIZ, AH, and NEP.
     * Len, Stride and LTPSIZE we just handled. Store those bits
     * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI
     * bits.
     */
    val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 |
        FPCR_EBF | FPCR_FIZ | FPCR_AH | FPCR_NEP;
    env->vfp.fpcr &= ~mask;
    env->vfp.fpcr |= val;
}

void vfp_set_fpcr(CPUARMState *env, uint32_t val)
{
    vfp_set_fpcr_masked(env, val, MAKE_64BIT_MASK(0, 32));
}

void vfp_set_fpscr(CPUARMState *env, uint32_t val)
{
    vfp_set_fpcr_masked(env, val, FPSCR_FPCR_MASK);
    vfp_set_fpsr(env, val & FPSCR_FPSR_MASK);
}