| /* |
| * Copyright (c) 2012-2014 Bastian Koppelmann C-Lab/University Paderborn |
| * |
| * 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 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 <stdlib.h> |
| #include "cpu.h" |
| #include "qemu/host-utils.h" |
| #include "exec/helper-proto.h" |
| #include "exec/cpu_ldst.h" |
| |
| /* Addressing mode helper */ |
| |
| static uint16_t reverse16(uint16_t val) |
| { |
| uint8_t high = (uint8_t)(val >> 8); |
| uint8_t low = (uint8_t)(val & 0xff); |
| |
| uint16_t rh, rl; |
| |
| rl = (uint16_t)((high * 0x0202020202ULL & 0x010884422010ULL) % 1023); |
| rh = (uint16_t)((low * 0x0202020202ULL & 0x010884422010ULL) % 1023); |
| |
| return (rh << 8) | rl; |
| } |
| |
| uint32_t helper_br_update(uint32_t reg) |
| { |
| uint32_t index = reg & 0xffff; |
| uint32_t incr = reg >> 16; |
| uint32_t new_index = reverse16(reverse16(index) + reverse16(incr)); |
| return reg - index + new_index; |
| } |
| |
| uint32_t helper_circ_update(uint32_t reg, uint32_t off) |
| { |
| uint32_t index = reg & 0xffff; |
| uint32_t length = reg >> 16; |
| int32_t new_index = index + off; |
| if (new_index < 0) { |
| new_index += length; |
| } else { |
| new_index %= length; |
| } |
| return reg - index + new_index; |
| } |
| |
| #define SSOV(env, ret, arg, len) do { \ |
| int64_t max_pos = INT##len ##_MAX; \ |
| int64_t max_neg = INT##len ##_MIN; \ |
| if (arg > max_pos) { \ |
| env->PSW_USB_V = (1 << 31); \ |
| env->PSW_USB_SV = (1 << 31); \ |
| ret = (target_ulong)max_pos; \ |
| } else { \ |
| if (arg < max_neg) { \ |
| env->PSW_USB_V = (1 << 31); \ |
| env->PSW_USB_SV = (1 << 31); \ |
| ret = (target_ulong)max_neg; \ |
| } else { \ |
| env->PSW_USB_V = 0; \ |
| ret = (target_ulong)arg; \ |
| } \ |
| } \ |
| env->PSW_USB_AV = arg ^ arg * 2u; \ |
| env->PSW_USB_SAV |= env->PSW_USB_AV; \ |
| } while (0) |
| |
| target_ulong helper_add_ssov(CPUTriCoreState *env, target_ulong r1, |
| target_ulong r2) |
| { |
| target_ulong ret; |
| int64_t t1 = sextract64(r1, 0, 32); |
| int64_t t2 = sextract64(r2, 0, 32); |
| int64_t result = t1 + t2; |
| SSOV(env, ret, result, 32); |
| return ret; |
| } |
| |
| target_ulong helper_sub_ssov(CPUTriCoreState *env, target_ulong r1, |
| target_ulong r2) |
| { |
| target_ulong ret; |
| int64_t t1 = sextract64(r1, 0, 32); |
| int64_t t2 = sextract64(r2, 0, 32); |
| int64_t result = t1 - t2; |
| SSOV(env, ret, result, 32); |
| return ret; |
| } |
| |
| /* context save area (CSA) related helpers */ |
| |
| static int cdc_increment(target_ulong *psw) |
| { |
| if ((*psw & MASK_PSW_CDC) == 0x7f) { |
| return 0; |
| } |
| |
| (*psw)++; |
| /* check for overflow */ |
| int lo = clo32((*psw & MASK_PSW_CDC) << (32 - 7)); |
| int mask = (1u << (7 - lo)) - 1; |
| int count = *psw & mask; |
| if (count == 0) { |
| (*psw)--; |
| return 1; |
| } |
| return 0; |
| } |
| |
| static int cdc_decrement(target_ulong *psw) |
| { |
| if ((*psw & MASK_PSW_CDC) == 0x7f) { |
| return 0; |
| } |
| /* check for underflow */ |
| int lo = clo32((*psw & MASK_PSW_CDC) << (32 - 7)); |
| int mask = (1u << (7 - lo)) - 1; |
| int count = *psw & mask; |
| if (count == 0) { |
| return 1; |
| } |
| (*psw)--; |
| return 0; |
| } |
| |
| static bool cdc_zero(target_ulong *psw) |
| { |
| int cdc = *psw & MASK_PSW_CDC; |
| /* Returns TRUE if PSW.CDC.COUNT == 0 or if PSW.CDC == |
| 7'b1111111, otherwise returns FALSE. */ |
| if (cdc == 0x7f) { |
| return true; |
| } |
| /* find CDC.COUNT */ |
| int lo = clo32((*psw & MASK_PSW_CDC) << (32 - 7)); |
| int mask = (1u << (7 - lo)) - 1; |
| int count = *psw & mask; |
| return count == 0; |
| } |
| |
| static void save_context_upper(CPUTriCoreState *env, int ea) |
| { |
| cpu_stl_data(env, ea, env->PCXI); |
| cpu_stl_data(env, ea+4, env->PSW); |
| cpu_stl_data(env, ea+8, env->gpr_a[10]); |
| cpu_stl_data(env, ea+12, env->gpr_a[11]); |
| cpu_stl_data(env, ea+16, env->gpr_d[8]); |
| cpu_stl_data(env, ea+20, env->gpr_d[9]); |
| cpu_stl_data(env, ea+24, env->gpr_d[10]); |
| cpu_stl_data(env, ea+28, env->gpr_d[11]); |
| cpu_stl_data(env, ea+32, env->gpr_a[12]); |
| cpu_stl_data(env, ea+36, env->gpr_a[13]); |
| cpu_stl_data(env, ea+40, env->gpr_a[14]); |
| cpu_stl_data(env, ea+44, env->gpr_a[15]); |
| cpu_stl_data(env, ea+48, env->gpr_d[12]); |
| cpu_stl_data(env, ea+52, env->gpr_d[13]); |
| cpu_stl_data(env, ea+56, env->gpr_d[14]); |
| cpu_stl_data(env, ea+60, env->gpr_d[15]); |
| } |
| |
| static void save_context_lower(CPUTriCoreState *env, int ea) |
| { |
| cpu_stl_data(env, ea, env->PCXI); |
| cpu_stl_data(env, ea+4, env->gpr_a[11]); |
| cpu_stl_data(env, ea+8, env->gpr_a[2]); |
| cpu_stl_data(env, ea+12, env->gpr_a[3]); |
| cpu_stl_data(env, ea+16, env->gpr_d[0]); |
| cpu_stl_data(env, ea+20, env->gpr_d[1]); |
| cpu_stl_data(env, ea+24, env->gpr_d[2]); |
| cpu_stl_data(env, ea+28, env->gpr_d[3]); |
| cpu_stl_data(env, ea+32, env->gpr_a[4]); |
| cpu_stl_data(env, ea+36, env->gpr_a[5]); |
| cpu_stl_data(env, ea+40, env->gpr_a[6]); |
| cpu_stl_data(env, ea+44, env->gpr_a[7]); |
| cpu_stl_data(env, ea+48, env->gpr_d[4]); |
| cpu_stl_data(env, ea+52, env->gpr_d[5]); |
| cpu_stl_data(env, ea+56, env->gpr_d[6]); |
| cpu_stl_data(env, ea+60, env->gpr_d[7]); |
| } |
| |
| static void restore_context_upper(CPUTriCoreState *env, int ea, |
| target_ulong *new_PCXI, target_ulong *new_PSW) |
| { |
| *new_PCXI = cpu_ldl_data(env, ea); |
| *new_PSW = cpu_ldl_data(env, ea+4); |
| env->gpr_a[10] = cpu_ldl_data(env, ea+8); |
| env->gpr_a[11] = cpu_ldl_data(env, ea+12); |
| env->gpr_d[8] = cpu_ldl_data(env, ea+16); |
| env->gpr_d[9] = cpu_ldl_data(env, ea+20); |
| env->gpr_d[10] = cpu_ldl_data(env, ea+24); |
| env->gpr_d[11] = cpu_ldl_data(env, ea+28); |
| env->gpr_a[12] = cpu_ldl_data(env, ea+32); |
| env->gpr_a[13] = cpu_ldl_data(env, ea+36); |
| env->gpr_a[14] = cpu_ldl_data(env, ea+40); |
| env->gpr_a[15] = cpu_ldl_data(env, ea+44); |
| env->gpr_d[12] = cpu_ldl_data(env, ea+48); |
| env->gpr_d[13] = cpu_ldl_data(env, ea+52); |
| env->gpr_d[14] = cpu_ldl_data(env, ea+56); |
| env->gpr_d[15] = cpu_ldl_data(env, ea+60); |
| } |
| |
| static void restore_context_lower(CPUTriCoreState *env, int ea, |
| target_ulong *ra, target_ulong *pcxi) |
| { |
| *pcxi = cpu_ldl_data(env, ea); |
| *ra = cpu_ldl_data(env, ea+4); |
| env->gpr_a[2] = cpu_ldl_data(env, ea+8); |
| env->gpr_a[3] = cpu_ldl_data(env, ea+12); |
| env->gpr_d[0] = cpu_ldl_data(env, ea+16); |
| env->gpr_d[1] = cpu_ldl_data(env, ea+20); |
| env->gpr_d[2] = cpu_ldl_data(env, ea+24); |
| env->gpr_d[3] = cpu_ldl_data(env, ea+28); |
| env->gpr_a[4] = cpu_ldl_data(env, ea+32); |
| env->gpr_a[5] = cpu_ldl_data(env, ea+36); |
| env->gpr_a[6] = cpu_ldl_data(env, ea+40); |
| env->gpr_a[7] = cpu_ldl_data(env, ea+44); |
| env->gpr_d[4] = cpu_ldl_data(env, ea+48); |
| env->gpr_d[5] = cpu_ldl_data(env, ea+52); |
| env->gpr_d[6] = cpu_ldl_data(env, ea+56); |
| env->gpr_d[7] = cpu_ldl_data(env, ea+60); |
| } |
| |
| void helper_call(CPUTriCoreState *env, uint32_t next_pc) |
| { |
| target_ulong tmp_FCX; |
| target_ulong ea; |
| target_ulong new_FCX; |
| target_ulong psw; |
| |
| psw = psw_read(env); |
| /* if (FCX == 0) trap(FCU); */ |
| if (env->FCX == 0) { |
| /* FCU trap */ |
| } |
| /* if (PSW.CDE) then if (cdc_increment()) then trap(CDO); */ |
| if (psw & MASK_PSW_CDE) { |
| if (cdc_increment(&psw)) { |
| /* CDO trap */ |
| } |
| } |
| /* PSW.CDE = 1;*/ |
| psw |= MASK_PSW_CDE; |
| /* tmp_FCX = FCX; */ |
| tmp_FCX = env->FCX; |
| /* EA = {FCX.FCXS, 6'b0, FCX.FCXO, 6'b0}; */ |
| ea = ((env->FCX & MASK_FCX_FCXS) << 12) + |
| ((env->FCX & MASK_FCX_FCXO) << 6); |
| /* new_FCX = M(EA, word); */ |
| new_FCX = cpu_ldl_data(env, ea); |
| /* M(EA, 16 * word) = {PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], |
| A[12], A[13], A[14], A[15], D[12], D[13], D[14], |
| D[15]}; */ |
| save_context_upper(env, ea); |
| |
| /* PCXI.PCPN = ICR.CCPN; */ |
| env->PCXI = (env->PCXI & 0xffffff) + |
| ((env->ICR & MASK_ICR_CCPN) << 24); |
| /* PCXI.PIE = ICR.IE; */ |
| env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE) + |
| ((env->ICR & MASK_ICR_IE) << 15)); |
| /* PCXI.UL = 1; */ |
| env->PCXI |= MASK_PCXI_UL; |
| |
| /* PCXI[19: 0] = FCX[19: 0]; */ |
| env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); |
| /* FCX[19: 0] = new_FCX[19: 0]; */ |
| env->FCX = (env->FCX & 0xfff00000) + (new_FCX & 0xfffff); |
| /* A[11] = next_pc[31: 0]; */ |
| env->gpr_a[11] = next_pc; |
| |
| /* if (tmp_FCX == LCX) trap(FCD);*/ |
| if (tmp_FCX == env->LCX) { |
| /* FCD trap */ |
| } |
| psw_write(env, psw); |
| } |
| |
| void helper_ret(CPUTriCoreState *env) |
| { |
| target_ulong ea; |
| target_ulong new_PCXI; |
| target_ulong new_PSW, psw; |
| |
| psw = psw_read(env); |
| /* if (PSW.CDE) then if (cdc_decrement()) then trap(CDU);*/ |
| if (env->PSW & MASK_PSW_CDE) { |
| if (cdc_decrement(&(env->PSW))) { |
| /* CDU trap */ |
| } |
| } |
| /* if (PCXI[19: 0] == 0) then trap(CSU); */ |
| if ((env->PCXI & 0xfffff) == 0) { |
| /* CSU trap */ |
| } |
| /* if (PCXI.UL == 0) then trap(CTYP); */ |
| if ((env->PCXI & MASK_PCXI_UL) == 0) { |
| /* CTYP trap */ |
| } |
| /* PC = {A11 [31: 1], 1’b0}; */ |
| env->PC = env->gpr_a[11] & 0xfffffffe; |
| |
| /* EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0}; */ |
| ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + |
| ((env->PCXI & MASK_PCXI_PCXO) << 6); |
| /* {new_PCXI, new_PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], |
| A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ |
| restore_context_upper(env, ea, &new_PCXI, &new_PSW); |
| /* M(EA, word) = FCX; */ |
| cpu_stl_data(env, ea, env->FCX); |
| /* FCX[19: 0] = PCXI[19: 0]; */ |
| env->FCX = (env->FCX & 0xfff00000) + (env->PCXI & 0x000fffff); |
| /* PCXI = new_PCXI; */ |
| env->PCXI = new_PCXI; |
| |
| if (tricore_feature(env, TRICORE_FEATURE_13)) { |
| /* PSW = new_PSW */ |
| psw_write(env, new_PSW); |
| } else { |
| /* PSW = {new_PSW[31:26], PSW[25:24], new_PSW[23:0]}; */ |
| psw_write(env, (new_PSW & ~(0x3000000)) + (psw & (0x3000000))); |
| } |
| } |
| |
| void helper_bisr(CPUTriCoreState *env, uint32_t const9) |
| { |
| target_ulong tmp_FCX; |
| target_ulong ea; |
| target_ulong new_FCX; |
| |
| if (env->FCX == 0) { |
| /* FCU trap */ |
| } |
| |
| tmp_FCX = env->FCX; |
| ea = ((env->FCX & 0xf0000) << 12) + ((env->FCX & 0xffff) << 6); |
| |
| /* new_FCX = M(EA, word); */ |
| new_FCX = cpu_ldl_data(env, ea); |
| /* M(EA, 16 * word) = {PCXI, A[11], A[2], A[3], D[0], D[1], D[2], D[3], A[4] |
| , A[5], A[6], A[7], D[4], D[5], D[6], D[7]}; */ |
| save_context_lower(env, ea); |
| |
| |
| /* PCXI.PCPN = ICR.CCPN */ |
| env->PCXI = (env->PCXI & 0xffffff) + |
| ((env->ICR & MASK_ICR_CCPN) << 24); |
| /* PCXI.PIE = ICR.IE */ |
| env->PCXI = ((env->PCXI & ~MASK_PCXI_PIE) + |
| ((env->ICR & MASK_ICR_IE) << 15)); |
| /* PCXI.UL = 0 */ |
| env->PCXI &= ~(MASK_PCXI_UL); |
| /* PCXI[19: 0] = FCX[19: 0] */ |
| env->PCXI = (env->PCXI & 0xfff00000) + (env->FCX & 0xfffff); |
| /* FXC[19: 0] = new_FCX[19: 0] */ |
| env->FCX = (env->FCX & 0xfff00000) + (new_FCX & 0xfffff); |
| /* ICR.IE = 1 */ |
| env->ICR |= MASK_ICR_IE; |
| |
| env->ICR |= const9; /* ICR.CCPN = const9[7: 0];*/ |
| |
| if (tmp_FCX == env->LCX) { |
| /* FCD trap */ |
| } |
| } |
| |
| void helper_rfe(CPUTriCoreState *env) |
| { |
| target_ulong ea; |
| target_ulong new_PCXI; |
| target_ulong new_PSW; |
| /* if (PCXI[19: 0] == 0) then trap(CSU); */ |
| if ((env->PCXI & 0xfffff) == 0) { |
| /* raise csu trap */ |
| } |
| /* if (PCXI.UL == 0) then trap(CTYP); */ |
| if ((env->PCXI & MASK_PCXI_UL) == 0) { |
| /* raise CTYP trap */ |
| } |
| /* if (!cdc_zero() AND PSW.CDE) then trap(NEST); */ |
| if (!cdc_zero(&(env->PSW)) && (env->PSW & MASK_PSW_CDE)) { |
| /* raise MNG trap */ |
| } |
| /* ICR.IE = PCXI.PIE; */ |
| env->ICR = (env->ICR & ~MASK_ICR_IE) + ((env->PCXI & MASK_PCXI_PIE) >> 15); |
| /* ICR.CCPN = PCXI.PCPN; */ |
| env->ICR = (env->ICR & ~MASK_ICR_CCPN) + |
| ((env->PCXI & MASK_PCXI_PCPN) >> 24); |
| /*EA = {PCXI.PCXS, 6'b0, PCXI.PCXO, 6'b0};*/ |
| ea = ((env->PCXI & MASK_PCXI_PCXS) << 12) + |
| ((env->PCXI & MASK_PCXI_PCXO) << 6); |
| /*{new_PCXI, PSW, A[10], A[11], D[8], D[9], D[10], D[11], A[12], |
| A[13], A[14], A[15], D[12], D[13], D[14], D[15]} = M(EA, 16 * word); */ |
| restore_context_upper(env, ea, &new_PCXI, &new_PSW); |
| /* M(EA, word) = FCX;*/ |
| cpu_stl_data(env, ea, env->FCX); |
| /* FCX[19: 0] = PCXI[19: 0]; */ |
| env->FCX = (env->FCX & 0xfff00000) + (env->PCXI & 0x000fffff); |
| /* PCXI = new_PCXI; */ |
| env->PCXI = new_PCXI; |
| /* write psw */ |
| psw_write(env, new_PSW); |
| } |
| |
| void helper_ldlcx(CPUTriCoreState *env, uint32_t ea) |
| { |
| uint32_t dummy; |
| /* insn doesn't load PCXI and RA */ |
| restore_context_lower(env, ea, &dummy, &dummy); |
| } |
| |
| void helper_lducx(CPUTriCoreState *env, uint32_t ea) |
| { |
| uint32_t dummy; |
| /* insn doesn't load PCXI and PSW */ |
| restore_context_upper(env, ea, &dummy, &dummy); |
| } |
| |
| void helper_stlcx(CPUTriCoreState *env, uint32_t ea) |
| { |
| save_context_lower(env, ea); |
| } |
| |
| void helper_stucx(CPUTriCoreState *env, uint32_t ea) |
| { |
| save_context_upper(env, ea); |
| } |
| |
| static inline void QEMU_NORETURN do_raise_exception_err(CPUTriCoreState *env, |
| uint32_t exception, |
| int error_code, |
| uintptr_t pc) |
| { |
| CPUState *cs = CPU(tricore_env_get_cpu(env)); |
| cs->exception_index = exception; |
| env->error_code = error_code; |
| |
| if (pc) { |
| /* now we have a real cpu fault */ |
| cpu_restore_state(cs, pc); |
| } |
| |
| cpu_loop_exit(cs); |
| } |
| |
| void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, |
| uintptr_t retaddr) |
| { |
| int ret; |
| ret = cpu_tricore_handle_mmu_fault(cs, addr, is_write, mmu_idx); |
| if (ret) { |
| TriCoreCPU *cpu = TRICORE_CPU(cs); |
| CPUTriCoreState *env = &cpu->env; |
| do_raise_exception_err(env, cs->exception_index, |
| env->error_code, retaddr); |
| } |
| } |