945a5bd3f8
Do not actually enable them so far, in terms of being able to change the current register set, but add all of the plumbing to address them. Do not enable them for user-only. Add an env->regs pointer that handles the indirection to the current register set. The naming of the pointer hides the difference between old and new, user-only and sysemu. From the notes on wrprs, which states that r0 must be initialized before use in shadow register sets, infer that R_ZERO is *not* hardwired to zero in shadow register sets, but that it is still read-only. Introduce tbflags bit R0_0 to track that it has been properly set to zero. Adjust load_gpr to reflect this. At the same time we might as well special case crs == 0 to avoid the indirection through env->regs during translation as well; this is intended to be the most common case for non-interrupt handlers. Init env->regs at reset. Signed-off-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-Id: <20220421151735.31996-54-richard.henderson@linaro.org>
306 lines
7.8 KiB
C
306 lines
7.8 KiB
C
/*
|
|
* Altera Nios II virtual CPU header
|
|
*
|
|
* Copyright (c) 2012 Chris Wulff <crwulff@gmail.com>
|
|
*
|
|
* 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/lgpl-2.1.html>
|
|
*/
|
|
|
|
#ifndef NIOS2_CPU_H
|
|
#define NIOS2_CPU_H
|
|
|
|
#include "exec/cpu-defs.h"
|
|
#include "hw/core/cpu.h"
|
|
#include "hw/registerfields.h"
|
|
#include "qom/object.h"
|
|
|
|
typedef struct CPUArchState CPUNios2State;
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
#include "mmu.h"
|
|
#endif
|
|
|
|
#define TYPE_NIOS2_CPU "nios2-cpu"
|
|
|
|
OBJECT_DECLARE_CPU_TYPE(Nios2CPU, Nios2CPUClass, NIOS2_CPU)
|
|
|
|
/**
|
|
* Nios2CPUClass:
|
|
* @parent_reset: The parent class' reset handler.
|
|
*
|
|
* A Nios2 CPU model.
|
|
*/
|
|
struct Nios2CPUClass {
|
|
/*< private >*/
|
|
CPUClass parent_class;
|
|
/*< public >*/
|
|
|
|
DeviceRealize parent_realize;
|
|
DeviceReset parent_reset;
|
|
};
|
|
|
|
#define TARGET_HAS_ICE 1
|
|
|
|
/* Configuration options for Nios II */
|
|
#define RESET_ADDRESS 0x00000000
|
|
#define EXCEPTION_ADDRESS 0x00000004
|
|
#define FAST_TLB_MISS_ADDRESS 0x00000008
|
|
|
|
#define NUM_GP_REGS 32
|
|
#define NUM_CR_REGS 32
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
/* 63 shadow register sets; index 0 is the primary register set. */
|
|
#define NUM_REG_SETS 64
|
|
#endif
|
|
|
|
/* General purpose register aliases */
|
|
enum {
|
|
R_ZERO = 0,
|
|
R_AT = 1,
|
|
R_RET0 = 2,
|
|
R_RET1 = 3,
|
|
R_ARG0 = 4,
|
|
R_ARG1 = 5,
|
|
R_ARG2 = 6,
|
|
R_ARG3 = 7,
|
|
R_ET = 24,
|
|
R_BT = 25,
|
|
R_GP = 26,
|
|
R_SP = 27,
|
|
R_FP = 28,
|
|
R_EA = 29,
|
|
R_BA = 30,
|
|
R_RA = 31,
|
|
};
|
|
|
|
/* Control register aliases */
|
|
enum {
|
|
CR_STATUS = 0,
|
|
CR_ESTATUS = 1,
|
|
CR_BSTATUS = 2,
|
|
CR_IENABLE = 3,
|
|
CR_IPENDING = 4,
|
|
CR_CPUID = 5,
|
|
CR_EXCEPTION = 7,
|
|
CR_PTEADDR = 8,
|
|
CR_TLBACC = 9,
|
|
CR_TLBMISC = 10,
|
|
CR_ENCINJ = 11,
|
|
CR_BADADDR = 12,
|
|
CR_CONFIG = 13,
|
|
CR_MPUBASE = 14,
|
|
CR_MPUACC = 15,
|
|
};
|
|
|
|
FIELD(CR_STATUS, PIE, 0, 1)
|
|
FIELD(CR_STATUS, U, 1, 1)
|
|
FIELD(CR_STATUS, EH, 2, 1)
|
|
FIELD(CR_STATUS, IH, 3, 1)
|
|
FIELD(CR_STATUS, IL, 4, 6)
|
|
FIELD(CR_STATUS, CRS, 10, 6)
|
|
FIELD(CR_STATUS, PRS, 16, 6)
|
|
FIELD(CR_STATUS, NMI, 22, 1)
|
|
FIELD(CR_STATUS, RSIE, 23, 1)
|
|
|
|
#define CR_STATUS_PIE R_CR_STATUS_PIE_MASK
|
|
#define CR_STATUS_U R_CR_STATUS_U_MASK
|
|
#define CR_STATUS_EH R_CR_STATUS_EH_MASK
|
|
#define CR_STATUS_IH R_CR_STATUS_IH_MASK
|
|
#define CR_STATUS_NMI R_CR_STATUS_NMI_MASK
|
|
#define CR_STATUS_RSIE R_CR_STATUS_RSIE_MASK
|
|
|
|
FIELD(CR_EXCEPTION, CAUSE, 2, 5)
|
|
FIELD(CR_EXCEPTION, ECCFTL, 31, 1)
|
|
|
|
FIELD(CR_PTEADDR, VPN, 2, 20)
|
|
FIELD(CR_PTEADDR, PTBASE, 22, 10)
|
|
|
|
FIELD(CR_TLBACC, PFN, 0, 20)
|
|
FIELD(CR_TLBACC, G, 20, 1)
|
|
FIELD(CR_TLBACC, X, 21, 1)
|
|
FIELD(CR_TLBACC, W, 22, 1)
|
|
FIELD(CR_TLBACC, R, 23, 1)
|
|
FIELD(CR_TLBACC, C, 24, 1)
|
|
FIELD(CR_TLBACC, IG, 25, 7)
|
|
|
|
#define CR_TLBACC_C R_CR_TLBACC_C_MASK
|
|
#define CR_TLBACC_R R_CR_TLBACC_R_MASK
|
|
#define CR_TLBACC_W R_CR_TLBACC_W_MASK
|
|
#define CR_TLBACC_X R_CR_TLBACC_X_MASK
|
|
#define CR_TLBACC_G R_CR_TLBACC_G_MASK
|
|
|
|
FIELD(CR_TLBMISC, D, 0, 1)
|
|
FIELD(CR_TLBMISC, PERM, 1, 1)
|
|
FIELD(CR_TLBMISC, BAD, 2, 1)
|
|
FIELD(CR_TLBMISC, DBL, 3, 1)
|
|
FIELD(CR_TLBMISC, PID, 4, 14)
|
|
FIELD(CR_TLBMISC, WE, 18, 1)
|
|
FIELD(CR_TLBMISC, RD, 19, 1)
|
|
FIELD(CR_TLBMISC, WAY, 20, 4)
|
|
FIELD(CR_TLBMISC, EE, 24, 1)
|
|
|
|
#define CR_TLBMISC_EE R_CR_TLBMISC_EE_MASK
|
|
#define CR_TLBMISC_RD R_CR_TLBMISC_RD_MASK
|
|
#define CR_TLBMISC_WE R_CR_TLBMISC_WE_MASK
|
|
#define CR_TLBMISC_DBL R_CR_TLBMISC_DBL_MASK
|
|
#define CR_TLBMISC_BAD R_CR_TLBMISC_BAD_MASK
|
|
#define CR_TLBMISC_PERM R_CR_TLBMISC_PERM_MASK
|
|
#define CR_TLBMISC_D R_CR_TLBMISC_D_MASK
|
|
|
|
/* Exceptions */
|
|
#define EXCP_BREAK 0x1000
|
|
#define EXCP_SEMIHOST 0x1001
|
|
#define EXCP_RESET 0
|
|
#define EXCP_PRESET 1
|
|
#define EXCP_IRQ 2
|
|
#define EXCP_TRAP 3
|
|
#define EXCP_UNIMPL 4
|
|
#define EXCP_ILLEGAL 5
|
|
#define EXCP_UNALIGN 6
|
|
#define EXCP_UNALIGND 7
|
|
#define EXCP_DIV 8
|
|
#define EXCP_SUPERA_X 9
|
|
#define EXCP_SUPERI 10
|
|
#define EXCP_SUPERA_D 11
|
|
#define EXCP_TLB_X 12
|
|
#define EXCP_TLB_D (0x1000 | EXCP_TLB_X)
|
|
#define EXCP_PERM_X 13
|
|
#define EXCP_PERM_R 14
|
|
#define EXCP_PERM_W 15
|
|
#define EXCP_MPUI 16
|
|
#define EXCP_MPUD 17
|
|
|
|
struct CPUArchState {
|
|
#ifdef CONFIG_USER_ONLY
|
|
uint32_t regs[NUM_GP_REGS];
|
|
#else
|
|
uint32_t shadow_regs[NUM_REG_SETS][NUM_GP_REGS];
|
|
/* Pointer into shadow_regs for the current register set. */
|
|
uint32_t *regs;
|
|
#endif
|
|
uint32_t ctrl[NUM_CR_REGS];
|
|
uint32_t pc;
|
|
|
|
#if !defined(CONFIG_USER_ONLY)
|
|
Nios2MMU mmu;
|
|
#endif
|
|
int error_code;
|
|
};
|
|
|
|
typedef struct {
|
|
uint32_t writable;
|
|
uint32_t readonly;
|
|
} ControlRegState;
|
|
|
|
/**
|
|
* Nios2CPU:
|
|
* @env: #CPUNios2State
|
|
*
|
|
* A Nios2 CPU.
|
|
*/
|
|
struct ArchCPU {
|
|
/*< private >*/
|
|
CPUState parent_obj;
|
|
/*< public >*/
|
|
|
|
CPUNegativeOffsetState neg;
|
|
CPUNios2State env;
|
|
|
|
bool diverr_present;
|
|
bool mmu_present;
|
|
|
|
uint32_t pid_num_bits;
|
|
uint32_t tlb_num_ways;
|
|
uint32_t tlb_num_entries;
|
|
|
|
/* Addresses that are hard-coded in the FPGA build settings */
|
|
uint32_t reset_addr;
|
|
uint32_t exception_addr;
|
|
uint32_t fast_tlb_miss_addr;
|
|
|
|
/* Bits within each control register which are reserved or readonly. */
|
|
ControlRegState cr_state[NUM_CR_REGS];
|
|
};
|
|
|
|
|
|
static inline bool nios2_cr_reserved(const ControlRegState *s)
|
|
{
|
|
return (s->writable | s->readonly) == 0;
|
|
}
|
|
|
|
static inline void nios2_update_crs(CPUNios2State *env)
|
|
{
|
|
#ifndef CONFIG_USER_ONLY
|
|
unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
|
|
env->regs = env->shadow_regs[crs];
|
|
#endif
|
|
}
|
|
|
|
void nios2_tcg_init(void);
|
|
void nios2_cpu_do_interrupt(CPUState *cs);
|
|
void dump_mmu(CPUNios2State *env);
|
|
void nios2_cpu_dump_state(CPUState *cpu, FILE *f, int flags);
|
|
hwaddr nios2_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
|
|
G_NORETURN void nios2_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
|
|
MMUAccessType access_type, int mmu_idx,
|
|
uintptr_t retaddr);
|
|
|
|
void do_nios2_semihosting(CPUNios2State *env);
|
|
|
|
#define CPU_RESOLVING_TYPE TYPE_NIOS2_CPU
|
|
|
|
#define cpu_gen_code cpu_nios2_gen_code
|
|
|
|
#define CPU_SAVE_VERSION 1
|
|
|
|
/* MMU modes definitions */
|
|
#define MMU_SUPERVISOR_IDX 0
|
|
#define MMU_USER_IDX 1
|
|
|
|
static inline int cpu_mmu_index(CPUNios2State *env, bool ifetch)
|
|
{
|
|
return (env->ctrl[CR_STATUS] & CR_STATUS_U) ? MMU_USER_IDX :
|
|
MMU_SUPERVISOR_IDX;
|
|
}
|
|
|
|
#ifndef CONFIG_USER_ONLY
|
|
bool nios2_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
|
|
MMUAccessType access_type, int mmu_idx,
|
|
bool probe, uintptr_t retaddr);
|
|
#endif
|
|
|
|
typedef CPUNios2State CPUArchState;
|
|
typedef Nios2CPU ArchCPU;
|
|
|
|
#include "exec/cpu-all.h"
|
|
|
|
FIELD(TBFLAGS, CRS0, 0, 1) /* Set if CRS == 0. */
|
|
FIELD(TBFLAGS, U, 1, 1) /* Overlaps CR_STATUS_U */
|
|
FIELD(TBFLAGS, R0_0, 2, 1) /* Set if R0 == 0. */
|
|
|
|
static inline void cpu_get_tb_cpu_state(CPUNios2State *env, target_ulong *pc,
|
|
target_ulong *cs_base, uint32_t *flags)
|
|
{
|
|
unsigned crs = FIELD_EX32(env->ctrl[CR_STATUS], CR_STATUS, CRS);
|
|
|
|
*pc = env->pc;
|
|
*cs_base = 0;
|
|
*flags = (env->ctrl[CR_STATUS] & CR_STATUS_U)
|
|
| (crs ? 0 : R_TBFLAGS_CRS0_MASK)
|
|
| (env->regs[0] ? 0 : R_TBFLAGS_R0_0_MASK);
|
|
}
|
|
|
|
#endif /* NIOS2_CPU_H */
|