aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--target-xtensa/cpu.h8
-rw-r--r--target-xtensa/helper.c1
-rw-r--r--target-xtensa/helpers.h8
-rw-r--r--target-xtensa/op_helper.c192
-rw-r--r--target-xtensa/translate.c145
5 files changed, 345 insertions, 9 deletions
diff --git a/target-xtensa/cpu.h b/target-xtensa/cpu.h
index cae663706b..7e662f5402 100644
--- a/target-xtensa/cpu.h
+++ b/target-xtensa/cpu.h
@@ -108,6 +108,8 @@ enum {
enum {
SAR = 3,
SCOMPARE1 = 12,
+ WINDOW_BASE = 72,
+ WINDOW_START = 73,
EPC1 = 177,
DEPC = 192,
EXCSAVE1 = 209,
@@ -134,6 +136,8 @@ enum {
#define PS_WOE 0x40000
+#define MAX_NAREG 64
+
enum {
/* Static vectors */
EXC_RESET,
@@ -185,6 +189,7 @@ enum {
typedef struct XtensaConfig {
const char *name;
uint64_t options;
+ unsigned nareg;
int excm_level;
int ndepc;
uint32_t exception_vector[EXC_MAX];
@@ -196,6 +201,7 @@ typedef struct CPUXtensaState {
uint32_t pc;
uint32_t sregs[256];
uint32_t uregs[256];
+ uint32_t phys_regs[MAX_NAREG];
int exception_taken;
@@ -214,6 +220,8 @@ int cpu_xtensa_exec(CPUXtensaState *s);
void do_interrupt(CPUXtensaState *s);
int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc);
void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf);
+void xtensa_sync_window_from_phys(CPUState *env);
+void xtensa_sync_phys_from_window(CPUState *env);
#define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt))
diff --git a/target-xtensa/helper.c b/target-xtensa/helper.c
index 44ebb9f29d..4f8693448e 100644
--- a/target-xtensa/helper.c
+++ b/target-xtensa/helper.c
@@ -45,6 +45,7 @@ static const XtensaConfig core_config[] = {
{
.name = "sample-xtensa-core",
.options = -1,
+ .nareg = 64,
.ndepc = 1,
.excm_level = 16,
.exception_vector = {
diff --git a/target-xtensa/helpers.h b/target-xtensa/helpers.h
index 6329c43ff6..0971fde8da 100644
--- a/target-xtensa/helpers.h
+++ b/target-xtensa/helpers.h
@@ -5,5 +5,13 @@ DEF_HELPER_2(exception_cause, void, i32, i32)
DEF_HELPER_3(exception_cause_vaddr, void, i32, i32, i32)
DEF_HELPER_1(nsa, i32, i32)
DEF_HELPER_1(nsau, i32, i32)
+DEF_HELPER_1(wsr_windowbase, void, i32)
+DEF_HELPER_3(entry, void, i32, i32, i32)
+DEF_HELPER_1(retw, i32, i32)
+DEF_HELPER_1(rotw, void, i32)
+DEF_HELPER_2(window_check, void, i32, i32)
+DEF_HELPER_0(restore_owb, void)
+DEF_HELPER_1(movsp, void, i32)
+DEF_HELPER_0(dump_state, void)
#include "def-helper.h"
diff --git a/target-xtensa/op_helper.c b/target-xtensa/op_helper.c
index 794a8340ca..7c3fb88a96 100644
--- a/target-xtensa/op_helper.c
+++ b/target-xtensa/op_helper.c
@@ -100,3 +100,195 @@ uint32_t HELPER(nsau)(uint32_t v)
{
return v ? clz32(v) : 32;
}
+
+static void copy_window_from_phys(CPUState *env,
+ uint32_t window, uint32_t phys, uint32_t n)
+{
+ assert(phys < env->config->nareg);
+ if (phys + n <= env->config->nareg) {
+ memcpy(env->regs + window, env->phys_regs + phys,
+ n * sizeof(uint32_t));
+ } else {
+ uint32_t n1 = env->config->nareg - phys;
+ memcpy(env->regs + window, env->phys_regs + phys,
+ n1 * sizeof(uint32_t));
+ memcpy(env->regs + window + n1, env->phys_regs,
+ (n - n1) * sizeof(uint32_t));
+ }
+}
+
+static void copy_phys_from_window(CPUState *env,
+ uint32_t phys, uint32_t window, uint32_t n)
+{
+ assert(phys < env->config->nareg);
+ if (phys + n <= env->config->nareg) {
+ memcpy(env->phys_regs + phys, env->regs + window,
+ n * sizeof(uint32_t));
+ } else {
+ uint32_t n1 = env->config->nareg - phys;
+ memcpy(env->phys_regs + phys, env->regs + window,
+ n1 * sizeof(uint32_t));
+ memcpy(env->phys_regs, env->regs + window + n1,
+ (n - n1) * sizeof(uint32_t));
+ }
+}
+
+
+static inline unsigned windowbase_bound(unsigned a, const CPUState *env)
+{
+ return a & (env->config->nareg / 4 - 1);
+}
+
+static inline unsigned windowstart_bit(unsigned a, const CPUState *env)
+{
+ return 1 << windowbase_bound(a, env);
+}
+
+void xtensa_sync_window_from_phys(CPUState *env)
+{
+ copy_window_from_phys(env, 0, env->sregs[WINDOW_BASE] * 4, 16);
+}
+
+void xtensa_sync_phys_from_window(CPUState *env)
+{
+ copy_phys_from_window(env, env->sregs[WINDOW_BASE] * 4, 0, 16);
+}
+
+static void rotate_window_abs(uint32_t position)
+{
+ xtensa_sync_phys_from_window(env);
+ env->sregs[WINDOW_BASE] = windowbase_bound(position, env);
+ xtensa_sync_window_from_phys(env);
+}
+
+static void rotate_window(uint32_t delta)
+{
+ rotate_window_abs(env->sregs[WINDOW_BASE] + delta);
+}
+
+void HELPER(wsr_windowbase)(uint32_t v)
+{
+ rotate_window_abs(v);
+}
+
+void HELPER(entry)(uint32_t pc, uint32_t s, uint32_t imm)
+{
+ int callinc = (env->sregs[PS] & PS_CALLINC) >> PS_CALLINC_SHIFT;
+ if (s > 3 || ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) {
+ qemu_log("Illegal entry instruction(pc = %08x), PS = %08x\n",
+ pc, env->sregs[PS]);
+ HELPER(exception_cause)(pc, ILLEGAL_INSTRUCTION_CAUSE);
+ } else {
+ env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - (imm << 3);
+ rotate_window(callinc);
+ env->sregs[WINDOW_START] |=
+ windowstart_bit(env->sregs[WINDOW_BASE], env);
+ }
+}
+
+void HELPER(window_check)(uint32_t pc, uint32_t w)
+{
+ uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env);
+ uint32_t windowstart = env->sregs[WINDOW_START];
+ uint32_t m, n;
+
+ if ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) {
+ return;
+ }
+
+ for (n = 1; ; ++n) {
+ if (n > w) {
+ return;
+ }
+ if (windowstart & windowstart_bit(windowbase + n, env)) {
+ break;
+ }
+ }
+
+ m = windowbase_bound(windowbase + n, env);
+ rotate_window(n);
+ env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) |
+ (windowbase << PS_OWB_SHIFT) | PS_EXCM;
+ env->sregs[EPC1] = env->pc = pc;
+
+ if (windowstart & windowstart_bit(m + 1, env)) {
+ HELPER(exception)(EXC_WINDOW_OVERFLOW4);
+ } else if (windowstart & windowstart_bit(m + 2, env)) {
+ HELPER(exception)(EXC_WINDOW_OVERFLOW8);
+ } else {
+ HELPER(exception)(EXC_WINDOW_OVERFLOW12);
+ }
+}
+
+uint32_t HELPER(retw)(uint32_t pc)
+{
+ int n = (env->regs[0] >> 30) & 0x3;
+ int m = 0;
+ uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env);
+ uint32_t windowstart = env->sregs[WINDOW_START];
+ uint32_t ret_pc = 0;
+
+ if (windowstart & windowstart_bit(windowbase - 1, env)) {
+ m = 1;
+ } else if (windowstart & windowstart_bit(windowbase - 2, env)) {
+ m = 2;
+ } else if (windowstart & windowstart_bit(windowbase - 3, env)) {
+ m = 3;
+ }
+
+ if (n == 0 || (m != 0 && m != n) ||
+ ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) {
+ qemu_log("Illegal retw instruction(pc = %08x), "
+ "PS = %08x, m = %d, n = %d\n",
+ pc, env->sregs[PS], m, n);
+ HELPER(exception_cause)(pc, ILLEGAL_INSTRUCTION_CAUSE);
+ } else {
+ int owb = windowbase;
+
+ ret_pc = (pc & 0xc0000000) | (env->regs[0] & 0x3fffffff);
+
+ rotate_window(-n);
+ if (windowstart & windowstart_bit(env->sregs[WINDOW_BASE], env)) {
+ env->sregs[WINDOW_START] &= ~windowstart_bit(owb, env);
+ } else {
+ /* window underflow */
+ env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) |
+ (windowbase << PS_OWB_SHIFT) | PS_EXCM;
+ env->sregs[EPC1] = env->pc = pc;
+
+ if (n == 1) {
+ HELPER(exception)(EXC_WINDOW_UNDERFLOW4);
+ } else if (n == 2) {
+ HELPER(exception)(EXC_WINDOW_UNDERFLOW8);
+ } else if (n == 3) {
+ HELPER(exception)(EXC_WINDOW_UNDERFLOW12);
+ }
+ }
+ }
+ return ret_pc;
+}
+
+void HELPER(rotw)(uint32_t imm4)
+{
+ rotate_window(imm4);
+}
+
+void HELPER(restore_owb)(void)
+{
+ rotate_window_abs((env->sregs[PS] & PS_OWB) >> PS_OWB_SHIFT);
+}
+
+void HELPER(movsp)(uint32_t pc)
+{
+ if ((env->sregs[WINDOW_START] &
+ (windowstart_bit(env->sregs[WINDOW_BASE] - 3, env) |
+ windowstart_bit(env->sregs[WINDOW_BASE] - 2, env) |
+ windowstart_bit(env->sregs[WINDOW_BASE] - 1, env))) == 0) {
+ HELPER(exception_cause)(pc, ALLOCA_CAUSE);
+ }
+}
+
+void HELPER(dump_state)(void)
+{
+ cpu_dump_state(env, stderr, fprintf, 0);
+}
diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c
index bc04a108b9..7a2e07ff60 100644
--- a/target-xtensa/translate.c
+++ b/target-xtensa/translate.c
@@ -67,6 +67,8 @@ static TCGv_i32 cpu_UR[256];
static const char * const sregnames[256] = {
[SAR] = "SAR",
[SCOMPARE1] = "SCOMPARE1",
+ [WINDOW_BASE] = "WINDOW_BASE",
+ [WINDOW_START] = "WINDOW_START",
[EPC1] = "EPC1",
[DEPC] = "DEPC",
[EXCSAVE1] = "EXCSAVE1",
@@ -217,6 +219,34 @@ static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot)
tcg_temp_free(tmp);
}
+static void gen_callw_slot(DisasContext *dc, int callinc, TCGv_i32 dest,
+ int slot)
+{
+ TCGv_i32 tcallinc = tcg_const_i32(callinc);
+
+ tcg_gen_deposit_i32(cpu_SR[PS], cpu_SR[PS],
+ tcallinc, PS_CALLINC_SHIFT, PS_CALLINC_LEN);
+ tcg_temp_free(tcallinc);
+ tcg_gen_movi_i32(cpu_R[callinc << 2],
+ (callinc << 30) | (dc->next_pc & 0x3fffffff));
+ gen_jump_slot(dc, dest, slot);
+}
+
+static void gen_callw(DisasContext *dc, int callinc, TCGv_i32 dest)
+{
+ gen_callw_slot(dc, callinc, dest, -1);
+}
+
+static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot)
+{
+ TCGv_i32 tmp = tcg_const_i32(dest);
+ if (((dc->pc ^ dest) & TARGET_PAGE_MASK) != 0) {
+ slot = -1;
+ }
+ gen_callw_slot(dc, callinc, tmp, slot);
+ tcg_temp_free(tmp);
+}
+
static void gen_brcond(DisasContext *dc, TCGCond cond,
TCGv_i32 t0, TCGv_i32 t1, uint32_t offset)
{
@@ -263,6 +293,11 @@ static void gen_wsr_sar(DisasContext *dc, uint32_t sr, TCGv_i32 s)
dc->sar_m32_5bit = false;
}
+static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v)
+{
+ gen_helper_wsr_windowbase(v);
+}
+
static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v)
{
uint32_t mask = PS_WOE | PS_CALLINC | PS_OWB |
@@ -281,6 +316,7 @@ static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s)
static void (* const wsr_handler[256])(DisasContext *dc,
uint32_t sr, TCGv_i32 v) = {
[SAR] = gen_wsr_sar,
+ [WINDOW_BASE] = gen_wsr_windowbase,
[PS] = gen_wsr_ps,
};
@@ -429,7 +465,12 @@ static void disas_xtensa_insn(DisasContext *dc)
case 1: /*RETWw*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ {
+ TCGv_i32 tmp = tcg_const_i32(dc->pc);
+ gen_helper_retw(tmp, tmp);
+ gen_jump(dc, tmp);
+ tcg_temp_free(tmp);
+ }
break;
case 3: /*reserved*/
@@ -454,7 +495,13 @@ static void disas_xtensa_insn(DisasContext *dc)
case 2: /*CALLX8w*/
case 3: /*CALLX12w*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ {
+ TCGv_i32 tmp = tcg_temp_new_i32();
+
+ tcg_gen_mov_i32(tmp, cpu_R[CALLX_S]);
+ gen_callw(dc, CALLX_N, tmp);
+ tcg_temp_free(tmp);
+ }
break;
}
break;
@@ -463,7 +510,12 @@ static void disas_xtensa_insn(DisasContext *dc)
case 1: /*MOVSPw*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ {
+ TCGv_i32 pc = tcg_const_i32(dc->pc);
+ gen_helper_movsp(pc);
+ tcg_gen_mov_i32(cpu_R[RRR_T], cpu_R[RRR_S]);
+ tcg_temp_free(pc);
+ }
break;
case 2: /*SYNC*/
@@ -523,7 +575,27 @@ static void disas_xtensa_insn(DisasContext *dc)
case 4: /*RFWOw*/
case 5: /*RFWUw*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ gen_check_privilege(dc);
+ {
+ TCGv_i32 tmp = tcg_const_i32(1);
+
+ tcg_gen_andi_i32(
+ cpu_SR[PS], cpu_SR[PS], ~PS_EXCM);
+ tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]);
+
+ if (RRR_S == 4) {
+ tcg_gen_andc_i32(cpu_SR[WINDOW_START],
+ cpu_SR[WINDOW_START], tmp);
+ } else {
+ tcg_gen_or_i32(cpu_SR[WINDOW_START],
+ cpu_SR[WINDOW_START], tmp);
+ }
+
+ gen_helper_restore_owb();
+ gen_jump(dc, cpu_SR[EPC1]);
+
+ tcg_temp_free(tmp);
+ }
break;
default: /*reserved*/
@@ -670,7 +742,13 @@ static void disas_xtensa_insn(DisasContext *dc)
case 8: /*ROTWw*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ gen_check_privilege(dc);
+ {
+ TCGv_i32 tmp = tcg_const_i32(
+ RRR_T | ((RRR_T & 8) ? 0xfffffff0 : 0));
+ gen_helper_rotw(tmp);
+ tcg_temp_free(tmp);
+ }
break;
case 14: /*NSAu*/
@@ -1129,7 +1207,35 @@ static void disas_xtensa_insn(DisasContext *dc)
break;
case 9: /*LSC4*/
- TBD();
+ switch (OP2) {
+ case 0: /*L32E*/
+ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+ gen_check_privilege(dc);
+ {
+ TCGv_i32 addr = tcg_temp_new_i32();
+ tcg_gen_addi_i32(addr, cpu_R[RRR_S],
+ (0xffffffc0 | (RRR_R << 2)));
+ tcg_gen_qemu_ld32u(cpu_R[RRR_T], addr, dc->ring);
+ tcg_temp_free(addr);
+ }
+ break;
+
+ case 4: /*S32E*/
+ HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
+ gen_check_privilege(dc);
+ {
+ TCGv_i32 addr = tcg_temp_new_i32();
+ tcg_gen_addi_i32(addr, cpu_R[RRR_S],
+ (0xffffffc0 | (RRR_R << 2)));
+ tcg_gen_qemu_st32(cpu_R[RRR_T], addr, dc->ring);
+ tcg_temp_free(addr);
+ }
+ break;
+
+ default:
+ RESERVED();
+ break;
+ }
break;
case 10: /*FP0*/
@@ -1368,7 +1474,8 @@ static void disas_xtensa_insn(DisasContext *dc)
case 2: /*CALL8w*/
case 3: /*CALL12w*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ gen_callwi(dc, CALL_N,
+ (dc->pc & ~3) + (CALL_OFFSET_SE << 2) + 4, 0);
break;
}
break;
@@ -1411,7 +1518,15 @@ static void disas_xtensa_insn(DisasContext *dc)
switch (BRI8_M) {
case 0: /*ENTRYw*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ {
+ TCGv_i32 pc = tcg_const_i32(dc->pc);
+ TCGv_i32 s = tcg_const_i32(BRI12_S);
+ TCGv_i32 imm = tcg_const_i32(BRI12_IMM12);
+ gen_helper_entry(pc, s, imm);
+ tcg_temp_free(imm);
+ tcg_temp_free(s);
+ tcg_temp_free(pc);
+ }
break;
case 1: /*B1*/
@@ -1576,7 +1691,12 @@ static void disas_xtensa_insn(DisasContext *dc)
case 1: /*RETW.Nn*/
HAS_OPTION(XTENSA_OPTION_WINDOWED_REGISTER);
- TBD();
+ {
+ TCGv_i32 tmp = tcg_const_i32(dc->pc);
+ gen_helper_retw(tmp, tmp);
+ gen_jump(dc, tmp);
+ tcg_temp_free(tmp);
+ }
break;
case 2: /*BREAK.Nn*/
@@ -1750,6 +1870,13 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf,
cpu_fprintf(f, "A%02d=%08x%c", i, env->regs[i],
(i % 4) == 3 ? '\n' : ' ');
}
+
+ cpu_fprintf(f, "\n");
+
+ for (i = 0; i < env->config->nareg; ++i) {
+ cpu_fprintf(f, "AR%02d=%08x%c", i, env->phys_regs[i],
+ (i % 4) == 3 ? '\n' : ' ');
+ }
}
void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos)