diff options
Diffstat (limited to 'target/nios2/translate.c')
-rw-r--r-- | target/nios2/translate.c | 958 |
1 files changed, 958 insertions, 0 deletions
diff --git a/target/nios2/translate.c b/target/nios2/translate.c new file mode 100644 index 0000000000..2d738391ad --- /dev/null +++ b/target/nios2/translate.c @@ -0,0 +1,958 @@ +/* + * Altera Nios II emulation for qemu: main translation routines. + * + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * Copyright (C) 2012 Chris Wulff <crwulff@gmail.com> + * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> + * (Portions of this file that were originally from nios2sim-ng.) + * + * 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> + */ + +#include "cpu.h" +#include "tcg-op.h" +#include "exec/exec-all.h" +#include "disas/disas.h" +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" +#include "exec/log.h" +#include "exec/cpu_ldst.h" + +#define INSTRUCTION_FLG(func, flags) { (func), (flags) } +#define INSTRUCTION(func) \ + INSTRUCTION_FLG(func, 0) +#define INSTRUCTION_NOP() \ + INSTRUCTION_FLG(nop, 0) +#define INSTRUCTION_UNIMPLEMENTED() \ + INSTRUCTION_FLG(gen_excp, EXCP_UNIMPL) +#define INSTRUCTION_ILLEGAL() \ + INSTRUCTION_FLG(gen_excp, EXCP_ILLEGAL) + +/* Special R-Type instruction opcode */ +#define INSN_R_TYPE 0x3A + +/* I-Type instruction parsing */ +#define I_TYPE(instr, code) \ + struct { \ + uint8_t op; \ + union { \ + uint16_t imm16; \ + int16_t imm16s; \ + }; \ + uint8_t b; \ + uint8_t a; \ + } (instr) = { \ + .op = extract32((code), 0, 6), \ + .imm16 = extract32((code), 6, 16), \ + .b = extract32((code), 22, 5), \ + .a = extract32((code), 27, 5), \ + } + +/* R-Type instruction parsing */ +#define R_TYPE(instr, code) \ + struct { \ + uint8_t op; \ + uint8_t imm5; \ + uint8_t opx; \ + uint8_t c; \ + uint8_t b; \ + uint8_t a; \ + } (instr) = { \ + .op = extract32((code), 0, 6), \ + .imm5 = extract32((code), 6, 5), \ + .opx = extract32((code), 11, 6), \ + .c = extract32((code), 17, 5), \ + .b = extract32((code), 22, 5), \ + .a = extract32((code), 27, 5), \ + } + +/* J-Type instruction parsing */ +#define J_TYPE(instr, code) \ + struct { \ + uint8_t op; \ + uint32_t imm26; \ + } (instr) = { \ + .op = extract32((code), 0, 6), \ + .imm26 = extract32((code), 6, 26), \ + } + +typedef struct DisasContext { + TCGv_ptr cpu_env; + TCGv *cpu_R; + TCGv_i32 zero; + int is_jmp; + target_ulong pc; + TranslationBlock *tb; + int mem_idx; + bool singlestep_enabled; +} DisasContext; + +typedef struct Nios2Instruction { + void (*handler)(DisasContext *dc, uint32_t code, uint32_t flags); + uint32_t flags; +} Nios2Instruction; + +static uint8_t get_opcode(uint32_t code) +{ + I_TYPE(instr, code); + return instr.op; +} + +static uint8_t get_opxcode(uint32_t code) +{ + R_TYPE(instr, code); + return instr.opx; +} + +static TCGv load_zero(DisasContext *dc) +{ + if (TCGV_IS_UNUSED_I32(dc->zero)) { + dc->zero = tcg_const_i32(0); + } + return dc->zero; +} + +static TCGv load_gpr(DisasContext *dc, uint8_t reg) +{ + if (likely(reg != R_ZERO)) { + return dc->cpu_R[reg]; + } else { + return load_zero(dc); + } +} + +static void t_gen_helper_raise_exception(DisasContext *dc, + uint32_t index) +{ + TCGv_i32 tmp = tcg_const_i32(index); + + tcg_gen_movi_tl(dc->cpu_R[R_PC], dc->pc); + gen_helper_raise_exception(dc->cpu_env, tmp); + tcg_temp_free_i32(tmp); + dc->is_jmp = DISAS_UPDATE; +} + +static bool use_goto_tb(DisasContext *dc, uint32_t dest) +{ + if (unlikely(dc->singlestep_enabled)) { + return false; + } + +#ifndef CONFIG_USER_ONLY + return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); +#else + return true; +#endif +} + +static void gen_goto_tb(DisasContext *dc, int n, uint32_t dest) +{ + TranslationBlock *tb = dc->tb; + + if (use_goto_tb(dc, dest)) { + tcg_gen_goto_tb(n); + tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); + tcg_gen_exit_tb((tcg_target_long)tb + n); + } else { + tcg_gen_movi_tl(dc->cpu_R[R_PC], dest); + tcg_gen_exit_tb(0); + } +} + +static void gen_excp(DisasContext *dc, uint32_t code, uint32_t flags) +{ + t_gen_helper_raise_exception(dc, flags); +} + +static void gen_check_supervisor(DisasContext *dc) +{ + if (dc->tb->flags & CR_STATUS_U) { + /* CPU in user mode, privileged instruction called, stop. */ + t_gen_helper_raise_exception(dc, EXCP_SUPERI); + } +} + +/* + * Used as a placeholder for all instructions which do not have + * an effect on the simulator (e.g. flush, sync) + */ +static void nop(DisasContext *dc, uint32_t code, uint32_t flags) +{ + /* Nothing to do here */ +} + +/* + * J-Type instructions + */ +static void jmpi(DisasContext *dc, uint32_t code, uint32_t flags) +{ + J_TYPE(instr, code); + gen_goto_tb(dc, 0, (dc->pc & 0xF0000000) | (instr.imm26 << 2)); + dc->is_jmp = DISAS_TB_JUMP; +} + +static void call(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); + jmpi(dc, code, flags); +} + +/* + * I-Type instructions + */ +/* Load instructions */ +static void gen_ldx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + + TCGv addr = tcg_temp_new(); + TCGv data; + + /* + * WARNING: Loads into R_ZERO are ignored, but we must generate the + * memory access itself to emulate the CPU precisely. Load + * from a protected page to R_ZERO will cause SIGSEGV on + * the Nios2 CPU. + */ + if (likely(instr.b != R_ZERO)) { + data = dc->cpu_R[instr.b]; + } else { + data = tcg_temp_new(); + } + + tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16s); + tcg_gen_qemu_ld_tl(data, addr, dc->mem_idx, flags); + + if (unlikely(instr.b == R_ZERO)) { + tcg_temp_free(data); + } + + tcg_temp_free(addr); +} + +/* Store instructions */ +static void gen_stx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + TCGv val = load_gpr(dc, instr.b); + + TCGv addr = tcg_temp_new(); + tcg_gen_addi_tl(addr, load_gpr(dc, instr.a), instr.imm16s); + tcg_gen_qemu_st_tl(val, addr, dc->mem_idx, flags); + tcg_temp_free(addr); +} + +/* Branch instructions */ +static void br(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + + gen_goto_tb(dc, 0, dc->pc + 4 + (instr.imm16s & -4)); + dc->is_jmp = DISAS_TB_JUMP; +} + +static void gen_bxx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + I_TYPE(instr, code); + + TCGLabel *l1 = gen_new_label(); + tcg_gen_brcond_tl(flags, dc->cpu_R[instr.a], dc->cpu_R[instr.b], l1); + gen_goto_tb(dc, 0, dc->pc + 4); + gen_set_label(l1); + gen_goto_tb(dc, 1, dc->pc + 4 + (instr.imm16s & -4)); + dc->is_jmp = DISAS_TB_JUMP; +} + +/* Comparison instructions */ +#define gen_i_cmpxx(fname, op3) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + I_TYPE(instr, (code)); \ + tcg_gen_setcondi_tl(flags, (dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \ + (op3)); \ +} + +gen_i_cmpxx(gen_cmpxxsi, instr.imm16s) +gen_i_cmpxx(gen_cmpxxui, instr.imm16) + +/* Math/logic instructions */ +#define gen_i_math_logic(fname, insn, resimm, op3) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + I_TYPE(instr, (code)); \ + if (unlikely(instr.b == R_ZERO)) { /* Store to R_ZERO is ignored */ \ + return; \ + } else if (instr.a == R_ZERO) { /* MOVxI optimizations */ \ + tcg_gen_movi_tl(dc->cpu_R[instr.b], (resimm) ? (op3) : 0); \ + } else { \ + tcg_gen_##insn##_tl((dc)->cpu_R[instr.b], (dc)->cpu_R[instr.a], \ + (op3)); \ + } \ +} + +gen_i_math_logic(addi, addi, 1, instr.imm16s) +gen_i_math_logic(muli, muli, 0, instr.imm16s) + +gen_i_math_logic(andi, andi, 0, instr.imm16) +gen_i_math_logic(ori, ori, 1, instr.imm16) +gen_i_math_logic(xori, xori, 1, instr.imm16) + +gen_i_math_logic(andhi, andi, 0, instr.imm16 << 16) +gen_i_math_logic(orhi , ori, 1, instr.imm16 << 16) +gen_i_math_logic(xorhi, xori, 1, instr.imm16 << 16) + +/* Prototype only, defined below */ +static void handle_r_type_instr(DisasContext *dc, uint32_t code, + uint32_t flags); + +static const Nios2Instruction i_type_instructions[] = { + INSTRUCTION(call), /* call */ + INSTRUCTION(jmpi), /* jmpi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbu */ + INSTRUCTION(addi), /* addi */ + INSTRUCTION_FLG(gen_stx, MO_UB), /* stb */ + INSTRUCTION(br), /* br */ + INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldb */ + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_GE), /* cmpgei */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhu */ + INSTRUCTION(andi), /* andi */ + INSTRUCTION_FLG(gen_stx, MO_UW), /* sth */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_GE), /* bge */ + INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldh */ + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_LT), /* cmplti */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_NOP(), /* initda */ + INSTRUCTION(ori), /* ori */ + INSTRUCTION_FLG(gen_stx, MO_UL), /* stw */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_LT), /* blt */ + INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldw */ + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_NE), /* cmpnei */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_NOP(), /* flushda */ + INSTRUCTION(xori), /* xori */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_bxx, TCG_COND_NE), /* bne */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_cmpxxsi, TCG_COND_EQ), /* cmpeqi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UB), /* ldbuio */ + INSTRUCTION(muli), /* muli */ + INSTRUCTION_FLG(gen_stx, MO_UB), /* stbio */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_EQ), /* beq */ + INSTRUCTION_FLG(gen_ldx, MO_SB), /* ldbio */ + INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_GEU), /* cmpgeui */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_ldx, MO_UW), /* ldhuio */ + INSTRUCTION(andhi), /* andhi */ + INSTRUCTION_FLG(gen_stx, MO_UW), /* sthio */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_GEU), /* bgeu */ + INSTRUCTION_FLG(gen_ldx, MO_SW), /* ldhio */ + INSTRUCTION_FLG(gen_cmpxxui, TCG_COND_LTU), /* cmpltui */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_UNIMPLEMENTED(), /* custom */ + INSTRUCTION_NOP(), /* initd */ + INSTRUCTION(orhi), /* orhi */ + INSTRUCTION_FLG(gen_stx, MO_SL), /* stwio */ + INSTRUCTION_FLG(gen_bxx, TCG_COND_LTU), /* bltu */ + INSTRUCTION_FLG(gen_ldx, MO_UL), /* ldwio */ + INSTRUCTION_UNIMPLEMENTED(), /* rdprs */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(handle_r_type_instr, 0), /* R-Type */ + INSTRUCTION_NOP(), /* flushd */ + INSTRUCTION(xorhi), /* xorhi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), +}; + +/* + * R-Type instructions + */ +/* + * status <- estatus + * PC <- ea + */ +static void eret(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_mov_tl(dc->cpu_R[CR_STATUS], dc->cpu_R[CR_ESTATUS]); + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_EA]); + + dc->is_jmp = DISAS_JUMP; +} + +/* PC <- ra */ +static void ret(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_RA]); + + dc->is_jmp = DISAS_JUMP; +} + +/* PC <- ba */ +static void bret(DisasContext *dc, uint32_t code, uint32_t flags) +{ + tcg_gen_mov_tl(dc->cpu_R[R_PC], dc->cpu_R[R_BA]); + + dc->is_jmp = DISAS_JUMP; +} + +/* PC <- rA */ +static void jmp(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a)); + + dc->is_jmp = DISAS_JUMP; +} + +/* rC <- PC + 4 */ +static void nextpc(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + if (likely(instr.c != R_ZERO)) { + tcg_gen_movi_tl(dc->cpu_R[instr.c], dc->pc + 4); + } +} + +/* + * ra <- PC + 4 + * PC <- rA + */ +static void callr(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + tcg_gen_mov_tl(dc->cpu_R[R_PC], load_gpr(dc, instr.a)); + tcg_gen_movi_tl(dc->cpu_R[R_RA], dc->pc + 4); + + dc->is_jmp = DISAS_JUMP; +} + +/* rC <- ctlN */ +static void rdctl(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + gen_check_supervisor(dc); + + switch (instr.imm5 + CR_BASE) { + case CR_PTEADDR: + case CR_TLBACC: + case CR_TLBMISC: + { +#if !defined(CONFIG_USER_ONLY) + if (likely(instr.c != R_ZERO)) { + tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + CR_BASE]); +#ifdef DEBUG_MMU + TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); + gen_helper_mmu_read_debug(dc->cpu_R[instr.c], dc->cpu_env, tmp); + tcg_temp_free_i32(tmp); +#endif + } +#endif + break; + } + + default: + if (likely(instr.c != R_ZERO)) { + tcg_gen_mov_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.imm5 + CR_BASE]); + } + break; + } +} + +/* ctlN <- rA */ +static void wrctl(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + + gen_check_supervisor(dc); + + switch (instr.imm5 + CR_BASE) { + case CR_PTEADDR: + case CR_TLBACC: + case CR_TLBMISC: + { +#if !defined(CONFIG_USER_ONLY) + TCGv_i32 tmp = tcg_const_i32(instr.imm5 + CR_BASE); + gen_helper_mmu_write(dc->cpu_env, tmp, load_gpr(dc, instr.a)); + tcg_temp_free_i32(tmp); +#endif + break; + } + + default: + tcg_gen_mov_tl(dc->cpu_R[instr.imm5 + CR_BASE], load_gpr(dc, instr.a)); + break; + } + + /* If interrupts were enabled using WRCTL, trigger them. */ +#if !defined(CONFIG_USER_ONLY) + if ((instr.imm5 + CR_BASE) == CR_STATUS) { + gen_helper_check_interrupts(dc->cpu_env); + } +#endif +} + +/* Comparison instructions */ +static void gen_cmpxx(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, code); + if (likely(instr.c != R_ZERO)) { + tcg_gen_setcond_tl(flags, dc->cpu_R[instr.c], dc->cpu_R[instr.a], + dc->cpu_R[instr.b]); + } +} + +/* Math/logic instructions */ +#define gen_r_math_logic(fname, insn, op3) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + R_TYPE(instr, (code)); \ + if (likely(instr.c != R_ZERO)) { \ + tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a), \ + (op3)); \ + } \ +} + +gen_r_math_logic(add, add_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(sub, sub_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(mul, mul_tl, load_gpr(dc, instr.b)) + +gen_r_math_logic(and, and_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(or, or_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(xor, xor_tl, load_gpr(dc, instr.b)) +gen_r_math_logic(nor, nor_tl, load_gpr(dc, instr.b)) + +gen_r_math_logic(srai, sari_tl, instr.imm5) +gen_r_math_logic(srli, shri_tl, instr.imm5) +gen_r_math_logic(slli, shli_tl, instr.imm5) +gen_r_math_logic(roli, rotli_tl, instr.imm5) + +#define gen_r_mul(fname, insn) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + R_TYPE(instr, (code)); \ + if (likely(instr.c != R_ZERO)) { \ + TCGv t0 = tcg_temp_new(); \ + tcg_gen_##insn(t0, dc->cpu_R[instr.c], \ + load_gpr(dc, instr.a), load_gpr(dc, instr.b)); \ + tcg_temp_free(t0); \ + } \ +} + +gen_r_mul(mulxss, muls2_tl) +gen_r_mul(mulxuu, mulu2_tl) +gen_r_mul(mulxsu, mulsu2_tl) + +#define gen_r_shift_s(fname, insn) \ +static void (fname)(DisasContext *dc, uint32_t code, uint32_t flags) \ +{ \ + R_TYPE(instr, (code)); \ + if (likely(instr.c != R_ZERO)) { \ + TCGv t0 = tcg_temp_new(); \ + tcg_gen_andi_tl(t0, load_gpr((dc), instr.b), 31); \ + tcg_gen_##insn((dc)->cpu_R[instr.c], load_gpr((dc), instr.a), t0); \ + tcg_temp_free(t0); \ + } \ +} + +gen_r_shift_s(sra, sar_tl) +gen_r_shift_s(srl, shr_tl) +gen_r_shift_s(sll, shl_tl) +gen_r_shift_s(rol, rotl_tl) +gen_r_shift_s(ror, rotr_tl) + +static void divs(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, (code)); + + /* Stores into R_ZERO are ignored */ + if (unlikely(instr.c == R_ZERO)) { + return; + } + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + + tcg_gen_ext32s_tl(t0, load_gpr(dc, instr.a)); + tcg_gen_ext32s_tl(t1, load_gpr(dc, instr.b)); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_tl(t2, t2, t3); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_tl(t2, t2, t3); + tcg_gen_movi_tl(t3, 0); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_div_tl(dc->cpu_R[instr.c], t0, t1); + tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]); + + tcg_temp_free(t3); + tcg_temp_free(t2); + tcg_temp_free(t1); + tcg_temp_free(t0); +} + +static void divu(DisasContext *dc, uint32_t code, uint32_t flags) +{ + R_TYPE(instr, (code)); + + /* Stores into R_ZERO are ignored */ + if (unlikely(instr.c == R_ZERO)) { + return; + } + + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_const_tl(0); + TCGv t3 = tcg_const_tl(1); + + tcg_gen_ext32u_tl(t0, load_gpr(dc, instr.a)); + tcg_gen_ext32u_tl(t1, load_gpr(dc, instr.b)); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_divu_tl(dc->cpu_R[instr.c], t0, t1); + tcg_gen_ext32s_tl(dc->cpu_R[instr.c], dc->cpu_R[instr.c]); + + tcg_temp_free(t3); + tcg_temp_free(t2); + tcg_temp_free(t1); + tcg_temp_free(t0); +} + +static const Nios2Instruction r_type_instructions[] = { + INSTRUCTION_ILLEGAL(), + INSTRUCTION(eret), /* eret */ + INSTRUCTION(roli), /* roli */ + INSTRUCTION(rol), /* rol */ + INSTRUCTION_NOP(), /* flushp */ + INSTRUCTION(ret), /* ret */ + INSTRUCTION(nor), /* nor */ + INSTRUCTION(mulxuu), /* mulxuu */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GE), /* cmpge */ + INSTRUCTION(bret), /* bret */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(ror), /* ror */ + INSTRUCTION_NOP(), /* flushi */ + INSTRUCTION(jmp), /* jmp */ + INSTRUCTION(and), /* and */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LT), /* cmplt */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(slli), /* slli */ + INSTRUCTION(sll), /* sll */ + INSTRUCTION_UNIMPLEMENTED(), /* wrprs */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(or), /* or */ + INSTRUCTION(mulxsu), /* mulxsu */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_NE), /* cmpne */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(srli), /* srli */ + INSTRUCTION(srl), /* srl */ + INSTRUCTION(nextpc), /* nextpc */ + INSTRUCTION(callr), /* callr */ + INSTRUCTION(xor), /* xor */ + INSTRUCTION(mulxss), /* mulxss */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_EQ), /* cmpeq */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION(divu), /* divu */ + INSTRUCTION(divs), /* div */ + INSTRUCTION(rdctl), /* rdctl */ + INSTRUCTION(mul), /* mul */ + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_GEU), /* cmpgeu */ + INSTRUCTION_NOP(), /* initi */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_excp, EXCP_TRAP), /* trap */ + INSTRUCTION(wrctl), /* wrctl */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_cmpxx, TCG_COND_LTU), /* cmpltu */ + INSTRUCTION(add), /* add */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_FLG(gen_excp, EXCP_BREAK), /* break */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION(nop), /* nop */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION(sub), /* sub */ + INSTRUCTION(srai), /* srai */ + INSTRUCTION(sra), /* sra */ + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), + INSTRUCTION_ILLEGAL(), +}; + +static void handle_r_type_instr(DisasContext *dc, uint32_t code, uint32_t flags) +{ + uint8_t opx; + const Nios2Instruction *instr; + + opx = get_opxcode(code); + if (unlikely(opx >= ARRAY_SIZE(r_type_instructions))) { + goto illegal_op; + } + + instr = &r_type_instructions[opx]; + instr->handler(dc, code, instr->flags); + + return; + +illegal_op: + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); +} + +static void handle_instruction(DisasContext *dc, CPUNios2State *env) +{ + uint32_t code; + uint8_t op; + const Nios2Instruction *instr; +#if defined(CONFIG_USER_ONLY) + /* FIXME: Is this needed ? */ + if (dc->pc >= 0x1000 && dc->pc < 0x2000) { + env->regs[R_PC] = dc->pc; + t_gen_helper_raise_exception(dc, 0xaa); + return; + } +#endif + code = cpu_ldl_code(env, dc->pc); + op = get_opcode(code); + + if (unlikely(op >= ARRAY_SIZE(i_type_instructions))) { + goto illegal_op; + } + + TCGV_UNUSED_I32(dc->zero); + + instr = &i_type_instructions[op]; + instr->handler(dc, code, instr->flags); + + if (!TCGV_IS_UNUSED_I32(dc->zero)) { + tcg_temp_free(dc->zero); + } + + return; + +illegal_op: + t_gen_helper_raise_exception(dc, EXCP_ILLEGAL); +} + +static const char * const regnames[] = { + "zero", "at", "r2", "r3", + "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", + "r20", "r21", "r22", "r23", + "et", "bt", "gp", "sp", + "fp", "ea", "ba", "ra", + "status", "estatus", "bstatus", "ienable", + "ipending", "cpuid", "reserved0", "exception", + "pteaddr", "tlbacc", "tlbmisc", "reserved1", + "badaddr", "config", "mpubase", "mpuacc", + "reserved2", "reserved3", "reserved4", "reserved5", + "reserved6", "reserved7", "reserved8", "reserved9", + "reserved10", "reserved11", "reserved12", "reserved13", + "reserved14", "reserved15", "reserved16", "reserved17", + "rpc" +}; + +static TCGv_ptr cpu_env; +static TCGv cpu_R[NUM_CORE_REGS]; + +#include "exec/gen-icount.h" + +static void gen_exception(DisasContext *dc, uint32_t excp) +{ + TCGv_i32 tmp = tcg_const_i32(excp); + + tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); + gen_helper_raise_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); + dc->is_jmp = DISAS_UPDATE; +} + +/* generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUNios2State *env, TranslationBlock *tb) +{ + Nios2CPU *cpu = nios2_env_get_cpu(env); + CPUState *cs = CPU(cpu); + DisasContext dc1, *dc = &dc1; + int num_insns; + int max_insns; + + /* Initialize DC */ + dc->cpu_env = cpu_env; + dc->cpu_R = cpu_R; + dc->is_jmp = DISAS_NEXT; + dc->pc = tb->pc; + dc->tb = tb; + dc->mem_idx = cpu_mmu_index(env, false); + dc->singlestep_enabled = cs->singlestep_enabled; + + /* Set up instruction counts */ + num_insns = 0; + if (cs->singlestep_enabled || singlestep) { + max_insns = 1; + } else { + int page_insns = (TARGET_PAGE_SIZE - (tb->pc & TARGET_PAGE_MASK)) / 4; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > page_insns) { + max_insns = page_insns; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + } + + gen_tb_start(tb); + do { + tcg_gen_insn_start(dc->pc); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + gen_exception(dc, EXCP_DEBUG); + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 4; + break; + } + + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + /* Decode an instruction */ + handle_instruction(dc, env); + + dc->pc += 4; + + /* Translation stops when a conditional branch is encountered. + * Otherwise the subsequent code could get translated several times. + * Also stop translation when a page boundary is reached. This + * ensures prefetch aborts occur at the right place. */ + } while (!dc->is_jmp && + !tcg_op_buf_full() && + num_insns < max_insns); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + /* Indicate where the next block should start */ + switch (dc->is_jmp) { + case DISAS_NEXT: + /* Save the current PC back into the CPU register */ + tcg_gen_movi_tl(cpu_R[R_PC], dc->pc); + tcg_gen_exit_tb(0); + break; + + default: + case DISAS_JUMP: + case DISAS_UPDATE: + /* The jump will already have updated the PC register */ + tcg_gen_exit_tb(0); + break; + + case DISAS_TB_JUMP: + /* nothing more to generate */ + break; + } + + /* End off the block */ + gen_tb_end(tb, num_insns); + + /* Mark instruction starts for the final generated instruction */ + tb->size = dc->pc - tb->pc; + tb->icount = num_insns; + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) + && qemu_log_in_addr_range(tb->pc)) { + qemu_log_lock(); + qemu_log("IN: %s\n", lookup_symbol(tb->pc)); + log_target_disas(cs, tb->pc, dc->pc - tb->pc, 0); + qemu_log("\n"); + qemu_log_unlock(); + } +#endif +} + +void nios2_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + Nios2CPU *cpu = NIOS2_CPU(cs); + CPUNios2State *env = &cpu->env; + int i; + + if (!env || !f) { + return; + } + + cpu_fprintf(f, "IN: PC=%x %s\n", + env->regs[R_PC], lookup_symbol(env->regs[R_PC])); + + for (i = 0; i < NUM_CORE_REGS; i++) { + cpu_fprintf(f, "%9s=%8.8x ", regnames[i], env->regs[i]); + if ((i + 1) % 4 == 0) { + cpu_fprintf(f, "\n"); + } + } +#if !defined(CONFIG_USER_ONLY) + cpu_fprintf(f, " mmu write: VPN=%05X PID %02X TLBACC %08X\n", + env->mmu.pteaddr_wr & CR_PTEADDR_VPN_MASK, + (env->mmu.tlbmisc_wr & CR_TLBMISC_PID_MASK) >> 4, + env->mmu.tlbacc_wr); +#endif + cpu_fprintf(f, "\n\n"); +} + +void nios2_tcg_init(void) +{ + int i; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + for (i = 0; i < NUM_CORE_REGS; i++) { + cpu_R[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUNios2State, regs[i]), + regnames[i]); + } +} + +void restore_state_to_opc(CPUNios2State *env, TranslationBlock *tb, + target_ulong *data) +{ + env->regs[R_PC] = data[0]; +} |