commit
a4fd257469
@ -0,0 +1,842 @@
|
||||
/* armcopro.c -- co-processor interface: ARM6 Instruction Emulator.
|
||||
Copyright (C) 1994, 2000 Advanced RISC Machines Ltd.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
#include "core/arm/interpreter/armos.h"
|
||||
#include "core/arm/interpreter/armemu.h"
|
||||
#include "core/arm/interpreter/vfp/vfp.h"
|
||||
|
||||
//chy 2005-07-08
|
||||
//#include "ansidecl.h"
|
||||
//chy -------
|
||||
//#include "iwmmxt.h"
|
||||
|
||||
|
||||
//chy 2005-09-19 add CP6 MRC support (for get irq number and base)
|
||||
extern unsigned xscale_cp6_mrc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * data);
|
||||
//chy 2005-09-19---------------
|
||||
|
||||
extern unsigned xscale_cp13_init (ARMul_State * state);
|
||||
extern unsigned xscale_cp13_exit (ARMul_State * state);
|
||||
extern unsigned xscale_cp13_ldc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword data);
|
||||
extern unsigned xscale_cp13_stc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * data);
|
||||
extern unsigned xscale_cp13_mrc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * data);
|
||||
extern unsigned xscale_cp13_mcr (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword data);
|
||||
extern unsigned xscale_cp13_cdp (ARMul_State * state, unsigned type,
|
||||
ARMword instr);
|
||||
extern unsigned xscale_cp13_read_reg (ARMul_State * state, unsigned reg,
|
||||
ARMword * data);
|
||||
extern unsigned xscale_cp13_write_reg (ARMul_State * state, unsigned reg,
|
||||
ARMword data);
|
||||
extern unsigned xscale_cp14_init (ARMul_State * state);
|
||||
extern unsigned xscale_cp14_exit (ARMul_State * state);
|
||||
extern unsigned xscale_cp14_ldc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword data);
|
||||
extern unsigned xscale_cp14_stc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * data);
|
||||
extern unsigned xscale_cp14_mrc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * data);
|
||||
extern unsigned xscale_cp14_mcr (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword data);
|
||||
extern unsigned xscale_cp14_cdp (ARMul_State * state, unsigned type,
|
||||
ARMword instr);
|
||||
extern unsigned xscale_cp14_read_reg (ARMul_State * state, unsigned reg,
|
||||
ARMword * data);
|
||||
extern unsigned xscale_cp14_write_reg (ARMul_State * state, unsigned reg,
|
||||
ARMword data);
|
||||
extern unsigned xscale_cp15_init (ARMul_State * state);
|
||||
extern unsigned xscale_cp15_exit (ARMul_State * state);
|
||||
extern unsigned xscale_cp15_ldc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword data);
|
||||
extern unsigned xscale_cp15_stc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * data);
|
||||
extern unsigned xscale_cp15_mrc (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * data);
|
||||
extern unsigned xscale_cp15_mcr (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword data);
|
||||
extern unsigned xscale_cp15_cdp (ARMul_State * state, unsigned type,
|
||||
ARMword instr);
|
||||
extern unsigned xscale_cp15_read_reg (ARMul_State * state, unsigned reg,
|
||||
ARMword * data);
|
||||
extern unsigned xscale_cp15_write_reg (ARMul_State * state, unsigned reg,
|
||||
ARMword data);
|
||||
extern unsigned xscale_cp15_cp_access_allowed (ARMul_State * state, unsigned reg,
|
||||
unsigned cpnum);
|
||||
|
||||
/* Dummy Co-processors. */
|
||||
|
||||
static unsigned
|
||||
NoCoPro3R (ARMul_State * state,
|
||||
unsigned a, ARMword b)
|
||||
{
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
NoCoPro4R (ARMul_State * state,
|
||||
unsigned a,
|
||||
ARMword b, ARMword c)
|
||||
{
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
NoCoPro4W (ARMul_State * state,
|
||||
unsigned a,
|
||||
ARMword b, ARMword * c)
|
||||
{
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
NoCoPro5R (ARMul_State * state,
|
||||
unsigned a,
|
||||
ARMword b,
|
||||
ARMword c, ARMword d)
|
||||
{
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
NoCoPro5W (ARMul_State * state,
|
||||
unsigned a,
|
||||
ARMword b,
|
||||
ARMword * c, ARMword * d )
|
||||
{
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
/* The XScale Co-processors. */
|
||||
|
||||
/* Coprocessor 15: System Control. */
|
||||
static void write_cp14_reg (unsigned, ARMword);
|
||||
static ARMword read_cp14_reg (unsigned);
|
||||
|
||||
/* There are two sets of registers for copro 15.
|
||||
One set is available when opcode_2 is 0 and
|
||||
the other set when opcode_2 >= 1. */
|
||||
static ARMword XScale_cp15_opcode_2_is_0_Regs[16];
|
||||
static ARMword XScale_cp15_opcode_2_is_not_0_Regs[16];
|
||||
/* There are also a set of breakpoint registers
|
||||
which are accessed via CRm instead of opcode_2. */
|
||||
static ARMword XScale_cp15_DBR1;
|
||||
static ARMword XScale_cp15_DBCON;
|
||||
static ARMword XScale_cp15_IBCR0;
|
||||
static ARMword XScale_cp15_IBCR1;
|
||||
|
||||
static unsigned
|
||||
XScale_cp15_init (ARMul_State * state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 16; i--;) {
|
||||
XScale_cp15_opcode_2_is_0_Regs[i] = 0;
|
||||
XScale_cp15_opcode_2_is_not_0_Regs[i] = 0;
|
||||
}
|
||||
|
||||
/* Initialise the processor ID. */
|
||||
//chy 2003-03-24, is same as cpu id in skyeye_options.c
|
||||
//XScale_cp15_opcode_2_is_0_Regs[0] = 0x69052000;
|
||||
XScale_cp15_opcode_2_is_0_Regs[0] = 0x69050000;
|
||||
|
||||
/* Initialise the cache type. */
|
||||
XScale_cp15_opcode_2_is_not_0_Regs[0] = 0x0B1AA1AA;
|
||||
|
||||
/* Initialise the ARM Control Register. */
|
||||
XScale_cp15_opcode_2_is_0_Regs[1] = 0x00000078;
|
||||
|
||||
return No_exp;
|
||||
}
|
||||
|
||||
/* Check an access to a register. */
|
||||
|
||||
static unsigned
|
||||
check_cp15_access (ARMul_State * state,
|
||||
unsigned reg,
|
||||
unsigned CRm, unsigned opcode_1, unsigned opcode_2)
|
||||
{
|
||||
/* Do not allow access to these register in USER mode. */
|
||||
//chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
|
||||
if (state->Mode == USER26MODE || state->Mode == USER32MODE )
|
||||
return ARMul_CANT;
|
||||
|
||||
/* Opcode_1should be zero. */
|
||||
if (opcode_1 != 0)
|
||||
return ARMul_CANT;
|
||||
|
||||
/* Different register have different access requirements. */
|
||||
switch (reg) {
|
||||
case 0:
|
||||
case 1:
|
||||
/* CRm must be 0. Opcode_2 can be anything. */
|
||||
if (CRm != 0)
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
/* CRm must be 0. Opcode_2 must be zero. */
|
||||
if ((CRm != 0) || (opcode_2 != 0))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 4:
|
||||
/* Access not allowed. */
|
||||
return ARMul_CANT;
|
||||
case 5:
|
||||
case 6:
|
||||
/* Opcode_2 must be zero. CRm must be 0. */
|
||||
if ((CRm != 0) || (opcode_2 != 0))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 7:
|
||||
/* Permissable combinations:
|
||||
Opcode_2 CRm
|
||||
0 5
|
||||
0 6
|
||||
0 7
|
||||
1 5
|
||||
1 6
|
||||
1 10
|
||||
4 10
|
||||
5 2
|
||||
6 5 */
|
||||
switch (opcode_2) {
|
||||
default:
|
||||
return ARMul_CANT;
|
||||
case 6:
|
||||
if (CRm != 5)
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 5:
|
||||
if (CRm != 2)
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 4:
|
||||
if (CRm != 10)
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 1:
|
||||
if ((CRm != 5) && (CRm != 6) && (CRm != 10))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 0:
|
||||
if ((CRm < 5) || (CRm > 7))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 8:
|
||||
/* Permissable combinations:
|
||||
Opcode_2 CRm
|
||||
0 5
|
||||
0 6
|
||||
0 7
|
||||
1 5
|
||||
1 6 */
|
||||
if (opcode_2 > 1)
|
||||
return ARMul_CANT;
|
||||
if ((CRm < 5) || (CRm > 7))
|
||||
return ARMul_CANT;
|
||||
if (opcode_2 == 1 && CRm == 7)
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 9:
|
||||
/* Opcode_2 must be zero or one. CRm must be 1 or 2. */
|
||||
if (((CRm != 0) && (CRm != 1))
|
||||
|| ((opcode_2 != 1) && (opcode_2 != 2)))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 10:
|
||||
/* Opcode_2 must be zero or one. CRm must be 4 or 8. */
|
||||
if (((CRm != 0) && (CRm != 1))
|
||||
|| ((opcode_2 != 4) && (opcode_2 != 8)))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 11:
|
||||
/* Access not allowed. */
|
||||
return ARMul_CANT;
|
||||
case 12:
|
||||
/* Access not allowed. */
|
||||
return ARMul_CANT;
|
||||
case 13:
|
||||
/* Opcode_2 must be zero. CRm must be 0. */
|
||||
if ((CRm != 0) || (opcode_2 != 0))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 14:
|
||||
/* Opcode_2 must be 0. CRm must be 0, 3, 4, 8 or 9. */
|
||||
if (opcode_2 != 0)
|
||||
return ARMul_CANT;
|
||||
|
||||
if ((CRm != 0) && (CRm != 3) && (CRm != 4) && (CRm != 8)
|
||||
&& (CRm != 9))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
case 15:
|
||||
/* Opcode_2 must be zero. CRm must be 1. */
|
||||
if ((CRm != 1) || (opcode_2 != 0))
|
||||
return ARMul_CANT;
|
||||
break;
|
||||
default:
|
||||
/* Should never happen. */
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
/* Coprocessor 13: Interrupt Controller and Bus Controller. */
|
||||
|
||||
/* There are two sets of registers for copro 13.
|
||||
One set (of three registers) is available when CRm is 0
|
||||
and the other set (of six registers) when CRm is 1. */
|
||||
|
||||
static ARMword XScale_cp13_CR0_Regs[16];
|
||||
static ARMword XScale_cp13_CR1_Regs[16];
|
||||
|
||||
static unsigned
|
||||
XScale_cp13_init (ARMul_State * state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 16; i--;) {
|
||||
XScale_cp13_CR0_Regs[i] = 0;
|
||||
XScale_cp13_CR1_Regs[i] = 0;
|
||||
}
|
||||
|
||||
return No_exp;
|
||||
}
|
||||
|
||||
/* Check an access to a register. */
|
||||
|
||||
static unsigned
|
||||
check_cp13_access (ARMul_State * state,
|
||||
unsigned reg,
|
||||
unsigned CRm, unsigned opcode_1, unsigned opcode_2)
|
||||
{
|
||||
/* Do not allow access to these registers in USER mode. */
|
||||
//chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
|
||||
if (state->Mode == USER26MODE || state->Mode == USER32MODE )
|
||||
return ARMul_CANT;
|
||||
|
||||
/* The opcodes should be zero. */
|
||||
if ((opcode_1 != 0) || (opcode_2 != 0))
|
||||
return ARMul_CANT;
|
||||
|
||||
/* Do not allow access to these register if bit
|
||||
13 of coprocessor 15's register 15 is zero. */
|
||||
if (!CP_ACCESS_ALLOWED (state, 13))
|
||||
return ARMul_CANT;
|
||||
|
||||
/* Registers 0, 4 and 8 are defined when CRm == 0.
|
||||
Registers 0, 1, 4, 5, 6, 7, 8 are defined when CRm == 1.
|
||||
For all other CRm values undefined behaviour results. */
|
||||
if (CRm == 0) {
|
||||
if (reg == 0 || reg == 4 || reg == 8)
|
||||
return ARMul_DONE;
|
||||
}
|
||||
else if (CRm == 1) {
|
||||
if (reg == 0 || reg == 1 || (reg >= 4 && reg <= 8))
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
/* Coprocessor 14: Performance Monitoring, Clock and Power management,
|
||||
Software Debug. */
|
||||
|
||||
static ARMword XScale_cp14_Regs[16];
|
||||
|
||||
static unsigned
|
||||
XScale_cp14_init (ARMul_State * state)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 16; i--;)
|
||||
XScale_cp14_Regs[i] = 0;
|
||||
|
||||
return No_exp;
|
||||
}
|
||||
|
||||
/* Check an access to a register. */
|
||||
|
||||
static unsigned
|
||||
check_cp14_access (ARMul_State * state,
|
||||
unsigned reg,
|
||||
unsigned CRm, unsigned opcode1, unsigned opcode2)
|
||||
{
|
||||
/* Not allowed to access these register in USER mode. */
|
||||
//chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
|
||||
if (state->Mode == USER26MODE || state->Mode == USER32MODE )
|
||||
return ARMul_CANT;
|
||||
|
||||
/* CRm should be zero. */
|
||||
if (CRm != 0)
|
||||
return ARMul_CANT;
|
||||
|
||||
/* OPcodes should be zero. */
|
||||
if (opcode1 != 0 || opcode2 != 0)
|
||||
return ARMul_CANT;
|
||||
|
||||
/* Accessing registers 4 or 5 has unpredicatable results. */
|
||||
if (reg >= 4 && reg <= 5)
|
||||
return ARMul_CANT;
|
||||
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
/* Here's ARMulator's MMU definition. A few things to note:
|
||||
1) It has eight registers, but only two are defined.
|
||||
2) You can only access its registers with MCR and MRC.
|
||||
3) MMU Register 0 (ID) returns 0x41440110
|
||||
4) Register 1 only has 4 bits defined. Bits 0 to 3 are unused, bit 4
|
||||
controls 32/26 bit program space, bit 5 controls 32/26 bit data space,
|
||||
bit 6 controls late abort timimg and bit 7 controls big/little endian. */
|
||||
|
||||
static ARMword MMUReg[8];
|
||||
|
||||
static unsigned
|
||||
MMUInit (ARMul_State * state)
|
||||
{
|
||||
/* 2004-05-09 chy
|
||||
-------------------------------------------------------------
|
||||
read ARM Architecture Reference Manual
|
||||
2.6.5 Data Abort
|
||||
There are three Abort Model in ARM arch.
|
||||
|
||||
Early Abort Model: used in some ARMv3 and earlier implementations. In this
|
||||
model, base register wirteback occurred for LDC,LDM,STC,STM instructions, and
|
||||
the base register was unchanged for all other instructions. (oldest)
|
||||
|
||||
Base Restored Abort Model: If a Data Abort occurs in an instruction which
|
||||
specifies base register writeback, the value in the base register is
|
||||
unchanged. (strongarm, xscale)
|
||||
|
||||
Base Updated Abort Model: If a Data Abort occurs in an instruction which
|
||||
specifies base register writeback, the base register writeback still occurs.
|
||||
(arm720T)
|
||||
|
||||
read PART B
|
||||
chap2 The System Control Coprocessor CP15
|
||||
2.4 Register1:control register
|
||||
L(bit 6): in some ARMv3 and earlier implementations, the abort model of the
|
||||
processor could be configured:
|
||||
0=early Abort Model Selected(now obsolete)
|
||||
1=Late Abort Model selceted(same as Base Updated Abort Model)
|
||||
|
||||
on later processors, this bit reads as 1 and ignores writes.
|
||||
-------------------------------------------------------------
|
||||
So, if lateabtSig=1, then it means Late Abort Model(Base Updated Abort Model)
|
||||
if lateabtSig=0, then it means Base Restored Abort Model
|
||||
because the ARMs which skyeye simulates are all belonged to ARMv4,
|
||||
so I think MMUReg[1]'s bit 6 should always be 1
|
||||
|
||||
*/
|
||||
|
||||
MMUReg[1] = state->prog32Sig << 4 |
|
||||
state->data32Sig << 5 | 1 << 6 | state->bigendSig << 7;
|
||||
//state->data32Sig << 5 | state->lateabtSig << 6 | state->bigendSig << 7;
|
||||
|
||||
|
||||
NOTICE_LOG(ARM11, "ARMul_ConsolePrint: MMU present");
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
MMUMRC (ARMul_State * state, unsigned type,
|
||||
ARMword instr, ARMword * value)
|
||||
{
|
||||
mmu_mrc (state, instr, value);
|
||||
return (ARMul_DONE);
|
||||
}
|
||||
|
||||
static unsigned
|
||||
MMUMCR (ARMul_State * state, unsigned type, ARMword instr, ARMword value)
|
||||
{
|
||||
mmu_mcr (state, instr, value);
|
||||
return (ARMul_DONE);
|
||||
}
|
||||
|
||||
/* What follows is the Validation Suite Coprocessor. It uses two
|
||||
co-processor numbers (4 and 5) and has the follwing functionality.
|
||||
Sixteen registers. Both co-processor nuimbers can be used in an MCR
|
||||
and MRC to access these registers. CP 4 can LDC and STC to and from
|
||||
the registers. CP 4 and CP 5 CDP 0 will busy wait for the number of
|
||||
cycles specified by a CP register. CP 5 CDP 1 issues a FIQ after a
|
||||
number of cycles (specified in a CP register), CDP 2 issues an IRQW
|
||||
in the same way, CDP 3 and 4 turn of the FIQ and IRQ source, and CDP 5
|
||||
stores a 32 bit time value in a CP register (actually it's the total
|
||||
number of N, S, I, C and F cyles). */
|
||||
|
||||
static ARMword ValReg[16];
|
||||
|
||||
static unsigned
|
||||
ValLDC (ARMul_State * state,
|
||||
unsigned type, ARMword instr, ARMword data)
|
||||
{
|
||||
static unsigned words;
|
||||
|
||||
if (type != ARMul_DATA)
|
||||
words = 0;
|
||||
else {
|
||||
ValReg[BITS (12, 15)] = data;
|
||||
|
||||
if (BIT (22))
|
||||
/* It's a long access, get two words. */
|
||||
if (words++ != 4)
|
||||
return ARMul_INC;
|
||||
}
|
||||
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
ValSTC (ARMul_State * state,
|
||||
unsigned type, ARMword instr, ARMword * data)
|
||||
{
|
||||
static unsigned words;
|
||||
|
||||
if (type != ARMul_DATA)
|
||||
words = 0;
|
||||
else {
|
||||
*data = ValReg[BITS (12, 15)];
|
||||
|
||||
if (BIT (22))
|
||||
/* It's a long access, get two words. */
|
||||
if (words++ != 4)
|
||||
return ARMul_INC;
|
||||
}
|
||||
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
ValMRC (ARMul_State * state,
|
||||
unsigned type, ARMword instr, ARMword * value)
|
||||
{
|
||||
*value = ValReg[BITS (16, 19)];
|
||||
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
ValMCR (ARMul_State * state,
|
||||
unsigned type, ARMword instr, ARMword value)
|
||||
{
|
||||
ValReg[BITS (16, 19)] = value;
|
||||
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
ValCDP (ARMul_State * state, unsigned type, ARMword instr)
|
||||
{
|
||||
static unsigned int finish = 0;
|
||||
|
||||
if (BITS (20, 23) != 0)
|
||||
return ARMul_CANT;
|
||||
|
||||
if (type == ARMul_FIRST) {
|
||||
ARMword howlong;
|
||||
|
||||
howlong = ValReg[BITS (0, 3)];
|
||||
|
||||
/* First cycle of a busy wait. */
|
||||
finish = ARMul_Time (state) + howlong;
|
||||
|
||||
return howlong == 0 ? ARMul_DONE : ARMul_BUSY;
|
||||
}
|
||||
else if (type == ARMul_BUSY) {
|
||||
if (ARMul_Time (state) >= finish)
|
||||
return ARMul_DONE;
|
||||
else
|
||||
return ARMul_BUSY;
|
||||
}
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
DoAFIQ (ARMul_State * state)
|
||||
{
|
||||
state->NfiqSig = LOW;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
DoAIRQ (ARMul_State * state)
|
||||
{
|
||||
state->NirqSig = LOW;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
IntCDP (ARMul_State * state, unsigned type, ARMword instr)
|
||||
{
|
||||
static unsigned int finish;
|
||||
ARMword howlong;
|
||||
|
||||
howlong = ValReg[BITS (0, 3)];
|
||||
|
||||
switch ((int) BITS (20, 23)) {
|
||||
case 0:
|
||||
if (type == ARMul_FIRST) {
|
||||
/* First cycle of a busy wait. */
|
||||
finish = ARMul_Time (state) + howlong;
|
||||
|
||||
return howlong == 0 ? ARMul_DONE : ARMul_BUSY;
|
||||
}
|
||||
else if (type == ARMul_BUSY) {
|
||||
if (ARMul_Time (state) >= finish)
|
||||
return ARMul_DONE;
|
||||
else
|
||||
return ARMul_BUSY;
|
||||
}
|
||||
return ARMul_DONE;
|
||||
|
||||
case 1:
|
||||
if (howlong == 0)
|
||||
ARMul_Abort (state, ARMul_FIQV);
|
||||
else
|
||||
ARMul_ScheduleEvent (state, howlong, DoAFIQ);
|
||||
return ARMul_DONE;
|
||||
|
||||
case 2:
|
||||
if (howlong == 0)
|
||||
ARMul_Abort (state, ARMul_IRQV);
|
||||
else
|
||||
ARMul_ScheduleEvent (state, howlong, DoAIRQ);
|
||||
return ARMul_DONE;
|
||||
|
||||
case 3:
|
||||
state->NfiqSig = HIGH;
|
||||
return ARMul_DONE;
|
||||
|
||||
case 4:
|
||||
state->NirqSig = HIGH;
|
||||
return ARMul_DONE;
|
||||
|
||||
case 5:
|
||||
ValReg[BITS (0, 3)] = ARMul_Time (state);
|
||||
return ARMul_DONE;
|
||||
}
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
/* Install co-processor instruction handlers in this routine. */
|
||||
|
||||
unsigned
|
||||
ARMul_CoProInit (ARMul_State * state)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* Initialise tham all first. */
|
||||
for (i = 0; i < 16; i++)
|
||||
ARMul_CoProDetach (state, i);
|
||||
|
||||
/* Install CoPro Instruction handlers here.
|
||||
The format is:
|
||||
ARMul_CoProAttach (state, CP Number, Init routine, Exit routine
|
||||
LDC routine, STC routine, MRC routine, MCR routine,
|
||||
CDP routine, Read Reg routine, Write Reg routine). */
|
||||
if (state->is_ep9312) {
|
||||
ARMul_CoProAttach (state, 4, NULL, NULL, DSPLDC4, DSPSTC4,
|
||||
DSPMRC4, DSPMCR4, NULL, NULL, DSPCDP4, NULL, NULL);
|
||||
ARMul_CoProAttach (state, 5, NULL, NULL, DSPLDC5, DSPSTC5,
|
||||
DSPMRC5, DSPMCR5, NULL, NULL, DSPCDP5, NULL, NULL);
|
||||
ARMul_CoProAttach (state, 6, NULL, NULL, NULL, NULL,
|
||||
DSPMRC6, DSPMCR6, NULL, NULL, DSPCDP6, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
ARMul_CoProAttach (state, 4, NULL, NULL, ValLDC, ValSTC,
|
||||
ValMRC, ValMCR, NULL, NULL, ValCDP, NULL, NULL);
|
||||
|
||||
ARMul_CoProAttach (state, 5, NULL, NULL, NULL, NULL,
|
||||
ValMRC, ValMCR, NULL, NULL, IntCDP, NULL, NULL);
|
||||
}
|
||||
|
||||
if (state->is_XScale) {
|
||||
//chy 2005-09-19, for PXA27x's CP6
|
||||
if (state->is_pxa27x) {
|
||||
ARMul_CoProAttach (state, 6, NULL, NULL,
|
||||
NULL, NULL, xscale_cp6_mrc,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
//chy 2005-09-19 end-------------
|
||||
ARMul_CoProAttach (state, 13, xscale_cp13_init,
|
||||
xscale_cp13_exit, xscale_cp13_ldc,
|
||||
xscale_cp13_stc, xscale_cp13_mrc,
|
||||
xscale_cp13_mcr, NULL, NULL, xscale_cp13_cdp,
|
||||
xscale_cp13_read_reg,
|
||||
xscale_cp13_write_reg);
|
||||
|
||||
ARMul_CoProAttach (state, 14, xscale_cp14_init,
|
||||
xscale_cp14_exit, xscale_cp14_ldc,
|
||||
xscale_cp14_stc, xscale_cp14_mrc,
|
||||
xscale_cp14_mcr, NULL, NULL, xscale_cp14_cdp,
|
||||
xscale_cp14_read_reg,
|
||||
xscale_cp14_write_reg);
|
||||
//chy: 2003-08-24.
|
||||
ARMul_CoProAttach (state, 15, xscale_cp15_init,
|
||||
xscale_cp15_exit, xscale_cp15_ldc,
|
||||
xscale_cp15_stc, xscale_cp15_mrc,
|
||||
xscale_cp15_mcr, NULL, NULL, xscale_cp15_cdp,
|
||||
xscale_cp15_read_reg,
|
||||
xscale_cp15_write_reg);
|
||||
}
|
||||
else if (state->is_v6) {
|
||||
ARMul_CoProAttach (state, 10, VFPInit, NULL, VFPLDC, VFPSTC,
|
||||
VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL);
|
||||
ARMul_CoProAttach (state, 11, VFPInit, NULL, VFPLDC, VFPSTC,
|
||||
VFPMRC, VFPMCR, VFPMRRC, VFPMCRR, VFPCDP, NULL, NULL);
|
||||
|
||||
ARMul_CoProAttach (state, 15, MMUInit, NULL, NULL, NULL,
|
||||
MMUMRC, MMUMCR, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
else { //all except xscale
|
||||
ARMul_CoProAttach (state, 15, MMUInit, NULL, NULL, NULL,
|
||||
// MMUMRC, MMUMCR, NULL, MMURead, MMUWrite);
|
||||
MMUMRC, MMUMCR, NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
//chy 2003-09-03 do it in future!!!!????
|
||||
#if 0
|
||||
if (state->is_iWMMXt) {
|
||||
ARMul_CoProAttach (state, 0, NULL, NULL, IwmmxtLDC, IwmmxtSTC,
|
||||
NULL, NULL, IwmmxtCDP, NULL, NULL);
|
||||
|
||||
ARMul_CoProAttach (state, 1, NULL, NULL, NULL, NULL,
|
||||
IwmmxtMRC, IwmmxtMCR, IwmmxtCDP, NULL,
|
||||
NULL);
|
||||
}
|
||||
#endif
|
||||
//-----------------------------------------------------------------------------
|
||||
//chy 2004-05-25, found the user/system code visit CP 1,2, so I add below code.
|
||||
ARMul_CoProAttach (state, 1, NULL, NULL, NULL, NULL,
|
||||
ValMRC, ValMCR, NULL, NULL, NULL, NULL, NULL);
|
||||
ARMul_CoProAttach (state, 2, NULL, NULL, ValLDC, ValSTC,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
//------------------------------------------------------------------------------
|
||||
/* No handlers below here. */
|
||||
|
||||
/* Call all the initialisation routines. */
|
||||
for (i = 0; i < 16; i++)
|
||||
if (state->CPInit[i])
|
||||
(state->CPInit[i]) (state);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Install co-processor finalisation routines in this routine. */
|
||||
|
||||
void
|
||||
ARMul_CoProExit (ARMul_State * state)
|
||||
{
|
||||
register unsigned i;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
if (state->CPExit[i])
|
||||
(state->CPExit[i]) (state);
|
||||
|
||||
for (i = 0; i < 16; i++) /* Detach all handlers. */
|
||||
ARMul_CoProDetach (state, i);
|
||||
}
|
||||
|
||||
/* Routines to hook Co-processors into ARMulator. */
|
||||
|
||||
void
|
||||
ARMul_CoProAttach (ARMul_State * state,
|
||||
unsigned number,
|
||||
ARMul_CPInits * init,
|
||||
ARMul_CPExits * exit,
|
||||
ARMul_LDCs * ldc,
|
||||
ARMul_STCs * stc,
|
||||
ARMul_MRCs * mrc,
|
||||
ARMul_MCRs * mcr,
|
||||
ARMul_MRRCs * mrrc,
|
||||
ARMul_MCRRs * mcrr,
|
||||
ARMul_CDPs * cdp,
|
||||
ARMul_CPReads * read, ARMul_CPWrites * write)
|
||||
{
|
||||
if (init != NULL)
|
||||
state->CPInit[number] = init;
|
||||
if (exit != NULL)
|
||||
state->CPExit[number] = exit;
|
||||
if (ldc != NULL)
|
||||
state->LDC[number] = ldc;
|
||||
if (stc != NULL)
|
||||
state->STC[number] = stc;
|
||||
if (mrc != NULL)
|
||||
state->MRC[number] = mrc;
|
||||
if (mcr != NULL)
|
||||
state->MCR[number] = mcr;
|
||||
if (mrrc != NULL)
|
||||
state->MRRC[number] = mrrc;
|
||||
if (mcrr != NULL)
|
||||
state->MCRR[number] = mcrr;
|
||||
if (cdp != NULL)
|
||||
state->CDP[number] = cdp;
|
||||
if (read != NULL)
|
||||
state->CPRead[number] = read;
|
||||
if (write != NULL)
|
||||
state->CPWrite[number] = write;
|
||||
}
|
||||
|
||||
void
|
||||
ARMul_CoProDetach (ARMul_State * state, unsigned number)
|
||||
{
|
||||
ARMul_CoProAttach (state, number, NULL, NULL,
|
||||
NoCoPro4R, NoCoPro4W, NoCoPro4W, NoCoPro4R,
|
||||
NoCoPro5W, NoCoPro5R, NoCoPro3R, NULL, NULL);
|
||||
|
||||
state->CPInit[number] = NULL;
|
||||
state->CPExit[number] = NULL;
|
||||
state->CPRead[number] = NULL;
|
||||
state->CPWrite[number] = NULL;
|
||||
}
|
||||
|
||||
//chy 2003-09-03:below funs just replace the old ones
|
||||
|
||||
/* Set the XScale FSR and FAR registers. */
|
||||
|
||||
void
|
||||
XScale_set_fsr_far (ARMul_State * state, ARMword fsr, ARMword _far)
|
||||
{
|
||||
//if (!state->is_XScale || (read_cp14_reg (10) & (1UL << 31)) == 0)
|
||||
if (!state->is_XScale)
|
||||
return;
|
||||
//assume opcode2=0 crm =0
|
||||
xscale_cp15_write_reg (state, 5, fsr);
|
||||
xscale_cp15_write_reg (state, 6, _far);
|
||||
}
|
||||
|
||||
//chy 2003-09-03 seems 0 is CANT, 1 is DONE ????
|
||||
int
|
||||
XScale_debug_moe (ARMul_State * state, int moe)
|
||||
{
|
||||
//chy 2003-09-03 , W/R CP14 reg, now it's no use ????
|
||||
printf ("SKYEYE: XScale_debug_moe called !!!!\n");
|
||||
return 1;
|
||||
}
|
@ -0,0 +1,370 @@
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
|
||||
/* mmu cache init
|
||||
*
|
||||
* @cache_t :cache_t to init
|
||||
* @width :cache line width in byte
|
||||
* @way :way of each cache set
|
||||
* @set :cache set num
|
||||
*
|
||||
* $ -1: error
|
||||
* 0: sucess
|
||||
*/
|
||||
int
|
||||
mmu_cache_init (cache_s * cache_t, int width, int way, int set, int w_mode)
|
||||
{
|
||||
int i, j;
|
||||
cache_set_t *sets;
|
||||
cache_line_t *lines;
|
||||
|
||||
/*alloc cache set */
|
||||
sets = NULL;
|
||||
lines = NULL;
|
||||
//fprintf(stderr, "mmu_cache_init: mallloc beg size %d,sets 0x%x\n", sizeof(cache_set_t) * set,sets);
|
||||
//exit(-1);
|
||||
sets = (cache_set_t *) malloc (sizeof (cache_set_t) * set);
|
||||
if (sets == NULL) {
|
||||
ERROR_LOG(ARM11, "set malloc size %d\n", sizeof (cache_set_t) * set);
|
||||
goto sets_error;
|
||||
}
|
||||
//fprintf(stderr, "mmu_cache_init: mallloc end sets 0x%x\n", sets);
|
||||
cache_t->sets = sets;
|
||||
|
||||
/*init cache set */
|
||||
for (i = 0; i < set; i++) {
|
||||
/*alloc cache line */
|
||||
lines = (cache_line_t *) malloc (sizeof (cache_line_t) * way);
|
||||
if (lines == NULL) {
|
||||
ERROR_LOG(ARM11, "line malloc size %d\n",
|
||||
sizeof (cache_line_t) * way);
|
||||
goto lines_error;
|
||||
}
|
||||
/*init cache line */
|
||||
for (j = 0; j < way; j++) {
|
||||
lines[j].tag = 0; //invalid
|
||||
lines[j].data = (ARMword *) malloc (width);
|
||||
if (lines[j].data == NULL) {
|
||||
ERROR_LOG(ARM11, "data alloc size %d\n", width);
|
||||
goto data_error;
|
||||
}
|
||||
}
|
||||
|
||||
sets[i].lines = lines;
|
||||
sets[i].cycle = 0;
|
||||
|
||||
}
|
||||
cache_t->width = width;
|
||||
cache_t->set = set;
|
||||
cache_t->way = way;
|
||||
cache_t->w_mode = w_mode;
|
||||
return 0;
|
||||
|
||||
data_error:
|
||||
/*free data */
|
||||
while (j-- > 0)
|
||||
free (lines[j].data);
|
||||
/*free data error line */
|
||||
free (lines);
|
||||
lines_error:
|
||||
/*free lines already alloced */
|
||||
while (i-- > 0) {
|
||||
for (j = 0; j < way; j++)
|
||||
free (sets[i].lines[j].data);
|
||||
free (sets[i].lines);
|
||||
}
|
||||
/*free sets */
|
||||
free (sets);
|
||||
sets_error:
|
||||
return -1;
|
||||
};
|
||||
|
||||
/* free a cache_t's inner data, the ptr self is not freed,
|
||||
* when needed do like below:
|
||||
* mmu_cache_exit(cache);
|
||||
* free(cache_t);
|
||||
*
|
||||
* @cache_t : the cache_t to free
|
||||
*/
|
||||
|
||||
void
|
||||
mmu_cache_exit (cache_s * cache_t)
|
||||
{
|
||||
int i, j;
|
||||
cache_set_t *sets, *set;
|
||||
cache_line_t *lines, *line;
|
||||
|
||||
/*free all set */
|
||||
sets = cache_t->sets;
|
||||
for (set = sets, i = 0; i < cache_t->set; i++, set++) {
|
||||
/*free all line */
|
||||
lines = set->lines;
|
||||
for (line = lines, j = 0; j < cache_t->way; j++, line++)
|
||||
free (line->data);
|
||||
free (lines);
|
||||
}
|
||||
free (sets);
|
||||
}
|
||||
|
||||
/* mmu cache search
|
||||
*
|
||||
* @state :ARMul_State
|
||||
* @cache_t :cache_t to search
|
||||
* @va :virtual address
|
||||
*
|
||||
* $ NULL: no cache match
|
||||
* cache :cache matched
|
||||
*/
|
||||
cache_line_t *
|
||||
mmu_cache_search (ARMul_State * state, cache_s * cache_t, ARMword va)
|
||||
{
|
||||
int i;
|
||||
int set = va_cache_set (va, cache_t);
|
||||
ARMword tag = va_cache_align (va, cache_t);
|
||||
cache_line_t *cache;
|
||||
|
||||
cache_set_t *cache_set = cache_t->sets + set;
|
||||
for (i = 0, cache = cache_set->lines; i < cache_t->way; i++, cache++) {
|
||||
if ((cache->tag & TAG_VALID_FLAG)
|
||||
&& (tag == va_cache_align (cache->tag, cache_t)))
|
||||
return cache;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mmu cache search by set/index
|
||||
*
|
||||
* @state :ARMul_State
|
||||
* @cache_t :cache_t to search
|
||||
* @index :set/index value.
|
||||
*
|
||||
* $ NULL: no cache match
|
||||
* cache :cache matched
|
||||
*/
|
||||
cache_line_t *
|
||||
mmu_cache_search_by_index (ARMul_State * state, cache_s * cache_t,
|
||||
ARMword index)
|
||||
{
|
||||
int way = cache_t->way;
|
||||
int set_v = index_cache_set (index, cache_t);
|
||||
int i = 0, index_v = 0;
|
||||
cache_set_t *set;
|
||||
|
||||
while ((way >>= 1) >= 1)
|
||||
i++;
|
||||
index_v = index >> (32 - i);
|
||||
set = cache_t->sets + set_v;
|
||||
|
||||
return set->lines + index_v;
|
||||
}
|
||||
|
||||
|
||||
/* mmu cache alloc
|
||||
*
|
||||
* @state :ARMul_State
|
||||
* @cache_t :cache_t to alloc from
|
||||
* @va :virtual address that require cache alloc, need not cache aligned
|
||||
* @pa :physical address of va
|
||||
*
|
||||
* $ cache_alloced, always alloc OK
|
||||
*/
|
||||
cache_line_t *
|
||||
mmu_cache_alloc (ARMul_State * state, cache_s * cache_t, ARMword va,
|
||||
ARMword pa)
|
||||
{
|
||||
cache_line_t *cache;
|
||||
cache_set_t *set;
|
||||
int i;
|
||||
|
||||
va = va_cache_align (va, cache_t);
|
||||
pa = va_cache_align (pa, cache_t);
|
||||
|
||||
set = &cache_t->sets[va_cache_set (va, cache_t)];
|
||||
|
||||
/*robin-round */
|
||||
cache = &set->lines[set->cycle++];
|
||||
if (set->cycle == cache_t->way)
|
||||
set->cycle = 0;
|
||||
|
||||
if (cache_t->w_mode == CACHE_WRITE_BACK) {
|
||||
ARMword t;
|
||||
|
||||
/*if cache valid, try to write back */
|
||||
if (cache->tag & TAG_VALID_FLAG) {
|
||||
mmu_cache_write_back (state, cache_t, cache);
|
||||
}
|
||||
/*read in cache_line */
|
||||
t = pa;
|
||||
for (i = 0; i < (cache_t->width >> WORD_SHT);
|
||||
i++, t += WORD_SIZE) {
|
||||
//cache->data[i] = mem_read_word (state, t);
|
||||
bus_read(32, t, &cache->data[i]);
|
||||
}
|
||||
}
|
||||
/*store tag and pa */
|
||||
cache->tag = va | TAG_VALID_FLAG;
|
||||
cache->pa = pa;
|
||||
|
||||
return cache;
|
||||
};
|
||||
|
||||
/* mmu_cache_write_back write cache data to memory
|
||||
* @state
|
||||
* @cache_t :cache_t of the cache line
|
||||
* @cache : cache line
|
||||
*/
|
||||
void
|
||||
mmu_cache_write_back (ARMul_State * state, cache_s * cache_t,
|
||||
cache_line_t * cache)
|
||||
{
|
||||
ARMword pa = cache->pa;
|
||||
int nw = cache_t->width >> WORD_SHT;
|
||||
ARMword *data = cache->data;
|
||||
int i;
|
||||
int t0, t1, t2;
|
||||
|
||||
if ((cache->tag & 1) == 0)
|
||||
return;
|
||||
|
||||
switch (cache->
|
||||
tag & ~1 & (TAG_FIRST_HALF_DIRTY | TAG_LAST_HALF_DIRTY)) {
|
||||
case 0:
|
||||
return;
|
||||
case TAG_FIRST_HALF_DIRTY:
|
||||
nw /= 2;
|
||||
break;
|
||||
case TAG_LAST_HALF_DIRTY:
|
||||
nw /= 2;
|
||||
pa += nw << WORD_SHT;
|
||||
data += nw;
|
||||
break;
|
||||
case TAG_FIRST_HALF_DIRTY | TAG_LAST_HALF_DIRTY:
|
||||
break;
|
||||
}
|
||||
for (i = 0; i < nw; i++, data++, pa += WORD_SIZE)
|
||||
//mem_write_word (state, pa, *data);
|
||||
bus_write(32, pa, *data);
|
||||
|
||||
cache->tag &= ~(TAG_FIRST_HALF_DIRTY | TAG_LAST_HALF_DIRTY);
|
||||
};
|
||||
|
||||
|
||||
/* mmu_cache_clean: clean a cache of va in cache_t
|
||||
*
|
||||
* @state :ARMul_State
|
||||
* @cache_t :cache_t to clean
|
||||
* @va :virtaul address
|
||||
*/
|
||||
void
|
||||
mmu_cache_clean (ARMul_State * state, cache_s * cache_t, ARMword va)
|
||||
{
|
||||
cache_line_t *cache;
|
||||
|
||||
cache = mmu_cache_search (state, cache_t, va);
|
||||
if (cache)
|
||||
mmu_cache_write_back (state, cache_t, cache);
|
||||
}
|
||||
|
||||
/* mmu_cache_clean_by_index: clean a cache by set/index format value
|
||||
*
|
||||
* @state :ARMul_State
|
||||
* @cache_t :cache_t to clean
|
||||
* @va :set/index format value
|
||||
*/
|
||||
void
|
||||
mmu_cache_clean_by_index (ARMul_State * state, cache_s * cache_t,
|
||||
ARMword index)
|
||||
{
|
||||
cache_line_t *cache;
|
||||
|
||||
cache = mmu_cache_search_by_index (state, cache_t, index);
|
||||
if (cache)
|
||||
mmu_cache_write_back (state, cache_t, cache);
|
||||
}
|
||||
|
||||
/* mmu_cache_invalidate : invalidate a cache of va
|
||||
*
|
||||
* @state :ARMul_State
|
||||
* @cache_t :cache_t to invalid
|
||||
* @va :virt_addr to invalid
|
||||
*/
|
||||
void
|
||||
mmu_cache_invalidate (ARMul_State * state, cache_s * cache_t, ARMword va)
|
||||
{
|
||||
cache_line_t *cache;
|
||||
|
||||
cache = mmu_cache_search (state, cache_t, va);
|
||||
if (cache) {
|
||||
mmu_cache_write_back (state, cache_t, cache);
|
||||
cache->tag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* mmu_cache_invalidate_by_index : invalidate a cache by index format
|
||||
*
|
||||
* @state :ARMul_State
|
||||
* @cache_t :cache_t to invalid
|
||||
* @index :set/index data
|
||||
*/
|
||||
void
|
||||
mmu_cache_invalidate_by_index (ARMul_State * state, cache_s * cache_t,
|
||||
ARMword index)
|
||||
{
|
||||
cache_line_t *cache;
|
||||
|
||||
cache = mmu_cache_search_by_index (state, cache_t, index);
|
||||
if (cache) {
|
||||
mmu_cache_write_back (state, cache_t, cache);
|
||||
cache->tag = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* mmu_cache_invalidate_all
|
||||
*
|
||||
* @state:
|
||||
* @cache_t
|
||||
* */
|
||||
void
|
||||
mmu_cache_invalidate_all (ARMul_State * state, cache_s * cache_t)
|
||||
{
|
||||
int i, j;
|
||||
cache_set_t *set;
|
||||
cache_line_t *cache;
|
||||
|
||||
set = cache_t->sets;
|
||||
for (i = 0; i < cache_t->set; i++, set++) {
|
||||
cache = set->lines;
|
||||
for (j = 0; j < cache_t->way; j++, cache++) {
|
||||
mmu_cache_write_back (state, cache_t, cache);
|
||||
cache->tag = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
mmu_cache_soft_flush (ARMul_State * state, cache_s * cache_t, ARMword pa)
|
||||
{
|
||||
ARMword set, way;
|
||||
cache_line_t *cache;
|
||||
pa = (pa / cache_t->width);
|
||||
way = pa & (cache_t->way - 1);
|
||||
set = (pa / cache_t->way) & (cache_t->set - 1);
|
||||
cache = &cache_t->sets[set].lines[way];
|
||||
|
||||
mmu_cache_write_back (state, cache_t, cache);
|
||||
cache->tag = 0;
|
||||
}
|
||||
|
||||
cache_line_t* mmu_cache_dirty_cache(ARMul_State *state,cache_s *cache){
|
||||
int i;
|
||||
int j;
|
||||
cache_line_t *cache_line = NULL;
|
||||
cache_set_t *cache_set = cache->sets;
|
||||
int sets = cache->set;
|
||||
for (i = 0; i < sets; i++){
|
||||
for(j = 0,cache_line = &cache_set[i].lines[0]; j < cache->way; j++,cache_line++){
|
||||
if((cache_line->tag & TAG_FIRST_HALF_DIRTY) || (cache_line->tag & TAG_LAST_HALF_DIRTY))
|
||||
return cache_line;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,126 @@
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
|
||||
/*chy 2004-06-06, fix bug found by wenye@cs.ucsb.edu*/
|
||||
ARMword rb_masks[] = {
|
||||
0x0, //RB_INVALID
|
||||
4, //RB_1
|
||||
16, //RB_4
|
||||
32, //RB_8
|
||||
};
|
||||
|
||||
/*mmu_rb_init
|
||||
* @rb_t :rb_t to init
|
||||
* @num :number of entry
|
||||
* */
|
||||
int
|
||||
mmu_rb_init (rb_s * rb_t, int num)
|
||||
{
|
||||
int i;
|
||||
rb_entry_t *entrys;
|
||||
|
||||
entrys = (rb_entry_t *) malloc (sizeof (*entrys) * num);
|
||||
if (entrys == NULL) {
|
||||
printf ("SKYEYE:mmu_rb_init malloc error\n");
|
||||
return -1;
|
||||
}
|
||||
for (i = 0; i < num; i++) {
|
||||
entrys[i].type = RB_INVALID;
|
||||
entrys[i].fault = NO_FAULT;
|
||||
}
|
||||
|
||||
rb_t->entrys = entrys;
|
||||
rb_t->num = num;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*mmu_rb_exit*/
|
||||
void
|
||||
mmu_rb_exit (rb_s * rb_t)
|
||||
{
|
||||
free (rb_t->entrys);
|
||||
};
|
||||
|
||||
/*mmu_rb_search
|
||||
* @rb_t :rb_t to serach
|
||||
* @va :va address to math
|
||||
*
|
||||
* $ NULL :not match
|
||||
* NO-NULL:
|
||||
* */
|
||||
rb_entry_t *
|
||||
mmu_rb_search (rb_s * rb_t, ARMword va)
|
||||
{
|
||||
int i;
|
||||
rb_entry_t *rb = rb_t->entrys;
|
||||
|
||||
DEBUG_LOG(ARM11, "va = %x\n", va);
|
||||
for (i = 0; i < rb_t->num; i++, rb++) {
|
||||
//2004-06-06 lyh bug from wenye@cs.ucsb.edu
|
||||
if (rb->type) {
|
||||
if ((va >= rb->va)
|
||||
&& (va < (rb->va + rb_masks[rb->type])))
|
||||
return rb;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
};
|
||||
|
||||
void
|
||||
mmu_rb_invalidate_entry (rb_s * rb_t, int i)
|
||||
{
|
||||
rb_t->entrys[i].type = RB_INVALID;
|
||||
}
|
||||
|
||||
void
|
||||
mmu_rb_invalidate_all (rb_s * rb_t)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < rb_t->num; i++)
|
||||
mmu_rb_invalidate_entry (rb_t, i);
|
||||
};
|
||||
|
||||
void
|
||||
mmu_rb_load (ARMul_State * state, rb_s * rb_t, int i_rb, int type, ARMword va)
|
||||
{
|
||||
rb_entry_t *rb;
|
||||
int i;
|
||||
ARMword max_start, min_end;
|
||||
fault_t fault;
|
||||
tlb_entry_t *tlb;
|
||||
|
||||
/*align va according to type */
|
||||
va &= ~(rb_masks[type] - 1);
|
||||
/*invalidate all RB match [va, va + rb_masks[type]] */
|
||||
for (rb = rb_t->entrys, i = 0; i < rb_t->num; i++, rb++) {
|
||||
if (rb->type) {
|
||||
max_start = max (va, rb->va);
|
||||
min_end =
|
||||
min (va + rb_masks[type],
|
||||
rb->va + rb_masks[rb->type]);
|
||||
if (max_start < min_end)
|
||||
rb->type = RB_INVALID;
|
||||
}
|
||||
}
|
||||
/*load word */
|
||||
rb = &rb_t->entrys[i_rb];
|
||||
rb->type = type;
|
||||
fault = translate (state, va, D_TLB (), &tlb);
|
||||
if (fault) {
|
||||
rb->fault = fault;
|
||||
return;
|
||||
}
|
||||
fault = check_access (state, va, tlb, 1);
|
||||
if (fault) {
|
||||
rb->fault = fault;
|
||||
return;
|
||||
}
|
||||
|
||||
rb->fault = NO_FAULT;
|
||||
va = tlb_va_to_pa (tlb, va);
|
||||
//2004-06-06 lyh bug from wenye@cs.ucsb.edu
|
||||
for (i = 0; i < (rb_masks[type] / 4); i++, va += WORD_SIZE) {
|
||||
//rb->data[i] = mem_read_word (state, va);
|
||||
bus_read(32, va, &rb->data[i]);
|
||||
};
|
||||
}
|
@ -0,0 +1,864 @@
|
||||
/*
|
||||
armmmu.c - Memory Management Unit emulation.
|
||||
ARMulator extensions for the ARM7100 family.
|
||||
Copyright (C) 1999 Ben Williamson
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
|
||||
/**
|
||||
* The interface of read data from bus
|
||||
*/
|
||||
int bus_read(short size, int addr, uint32_t * value) {
|
||||
ERROR_LOG(ARM11, "unimplemented bus_read");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* The interface of write data from bus
|
||||
*/
|
||||
int bus_write(short size, int addr, uint32_t value) {
|
||||
ERROR_LOG(ARM11, "unimplemented bus_write");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
typedef struct sa_mmu_desc_s
|
||||
{
|
||||
int i_tlb;
|
||||
cache_desc_t i_cache;
|
||||
|
||||
int d_tlb;
|
||||
cache_desc_t main_d_cache;
|
||||
cache_desc_t mini_d_cache;
|
||||
int rb;
|
||||
wb_desc_t wb;
|
||||
} sa_mmu_desc_t;
|
||||
|
||||
static sa_mmu_desc_t sa11xx_mmu_desc = {
|
||||
32,
|
||||
{32, 32, 16, CACHE_WRITE_BACK},
|
||||
|
||||
32,
|
||||
{32, 32, 8, CACHE_WRITE_BACK},
|
||||
{32, 2, 8, CACHE_WRITE_BACK},
|
||||
4,
|
||||
//{8, 4}, for word size
|
||||
{8, 16}, //for byte size, chy 2003-07-11
|
||||
};
|
||||
|
||||
static fault_t sa_mmu_write (ARMul_State * state, ARMword va, ARMword data,
|
||||
ARMword datatype);
|
||||
static fault_t sa_mmu_read (ARMul_State * state, ARMword va, ARMword * data,
|
||||
ARMword datatype);
|
||||
static fault_t update_cache (ARMul_State * state, ARMword va, ARMword data,
|
||||
ARMword datatype, cache_line_t * cache,
|
||||
cache_s * cache_t, ARMword real_va);
|
||||
|
||||
void
|
||||
mmu_wb_write_bytes (ARMul_State * state, wb_s * wb_t, ARMword pa,
|
||||
ARMbyte * data, int n);
|
||||
int
|
||||
sa_mmu_init (ARMul_State * state)
|
||||
{
|
||||
sa_mmu_desc_t *desc;
|
||||
cache_desc_t *c_desc;
|
||||
|
||||
state->mmu.control = 0x70;
|
||||
state->mmu.translation_table_base = 0xDEADC0DE;
|
||||
state->mmu.domain_access_control = 0xDEADC0DE;
|
||||
state->mmu.fault_status = 0;
|
||||
state->mmu.fault_address = 0;
|
||||
state->mmu.process_id = 0;
|
||||
|
||||
desc = &sa11xx_mmu_desc;
|
||||
if (mmu_tlb_init (I_TLB (), desc->i_tlb)) {
|
||||
ERROR_LOG(ARM11, "i_tlb init %d\n", -1);
|
||||
goto i_tlb_init_error;
|
||||
}
|
||||
|
||||
c_desc = &desc->i_cache;
|
||||
if (mmu_cache_init (I_CACHE (), c_desc->width, c_desc->way,
|
||||
c_desc->set, c_desc->w_mode)) {
|
||||
ERROR_LOG(ARM11, "i_cache init %d\n", -1);
|
||||
goto i_cache_init_error;
|
||||
}
|
||||
|
||||
if (mmu_tlb_init (D_TLB (), desc->d_tlb)) {
|
||||
ERROR_LOG(ARM11, "d_tlb init %d\n", -1);
|
||||
goto d_tlb_init_error;
|
||||
}
|
||||
|
||||
c_desc = &desc->main_d_cache;
|
||||
if (mmu_cache_init (MAIN_D_CACHE (), c_desc->width, c_desc->way,
|
||||
c_desc->set, c_desc->w_mode)) {
|
||||
ERROR_LOG(ARM11, "main_d_cache init %d\n", -1);
|
||||
goto main_d_cache_init_error;
|
||||
}
|
||||
|
||||
c_desc = &desc->mini_d_cache;
|
||||
if (mmu_cache_init (MINI_D_CACHE (), c_desc->width, c_desc->way,
|
||||
c_desc->set, c_desc->w_mode)) {
|
||||
ERROR_LOG(ARM11, "mini_d_cache init %d\n", -1);
|
||||
goto mini_d_cache_init_error;
|
||||
}
|
||||
|
||||
if (mmu_wb_init (WB (), desc->wb.num, desc->wb.nb)) {
|
||||
ERROR_LOG(ARM11, "wb init %d\n", -1);
|
||||
goto wb_init_error;
|
||||
}
|
||||
|
||||
if (mmu_rb_init (RB (), desc->rb)) {
|
||||
ERROR_LOG(ARM11, "rb init %d\n", -1);
|
||||
goto rb_init_error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
rb_init_error:
|
||||
mmu_wb_exit (WB ());
|
||||
wb_init_error:
|
||||
mmu_cache_exit (MINI_D_CACHE ());
|
||||
mini_d_cache_init_error:
|
||||
mmu_cache_exit (MAIN_D_CACHE ());
|
||||
main_d_cache_init_error:
|
||||
mmu_tlb_exit (D_TLB ());
|
||||
d_tlb_init_error:
|
||||
mmu_cache_exit (I_CACHE ());
|
||||
i_cache_init_error:
|
||||
mmu_tlb_exit (I_TLB ());
|
||||
i_tlb_init_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
sa_mmu_exit (ARMul_State * state)
|
||||
{
|
||||
mmu_rb_exit (RB ());
|
||||
mmu_wb_exit (WB ());
|
||||
mmu_cache_exit (MINI_D_CACHE ());
|
||||
mmu_cache_exit (MAIN_D_CACHE ());
|
||||
mmu_tlb_exit (D_TLB ());
|
||||
mmu_cache_exit (I_CACHE ());
|
||||
mmu_tlb_exit (I_TLB ());
|
||||
};
|
||||
|
||||
|
||||
static fault_t
|
||||
sa_mmu_load_instr (ARMul_State * state, ARMword va, ARMword * instr)
|
||||
{
|
||||
fault_t fault;
|
||||
tlb_entry_t *tlb;
|
||||
cache_line_t *cache;
|
||||
int c; //cache bit
|
||||
ARMword pa; //physical addr
|
||||
|
||||
static int debug_count = 0; //used for debug
|
||||
|
||||
DEBUG_LOG(ARM11, "va = %x\n", va);
|
||||
|
||||
va = mmu_pid_va_map (va);
|
||||
if (MMU_Enabled) {
|
||||
/*align check */
|
||||
if ((va & (WORD_SIZE - 1)) && MMU_Aligned) {
|
||||
DEBUG_LOG(ARM11, "align\n");
|
||||
return ALIGNMENT_FAULT;
|
||||
}
|
||||
else
|
||||
va &= ~(WORD_SIZE - 1);
|
||||
|
||||
/*translate tlb */
|
||||
fault = translate (state, va, I_TLB (), &tlb);
|
||||
if (fault) {
|
||||
DEBUG_LOG(ARM11, "translate\n");
|
||||
return fault;
|
||||
}
|
||||
|
||||
/*check access */
|
||||
fault = check_access (state, va, tlb, 1);
|
||||
if (fault) {
|
||||
DEBUG_LOG(ARM11, "check_fault\n");
|
||||
return fault;
|
||||
}
|
||||
}
|
||||
|
||||
/*search cache no matter MMU enabled/disabled */
|
||||
cache = mmu_cache_search (state, I_CACHE (), va);
|
||||
if (cache) {
|
||||
*instr = cache->data[va_cache_index (va, I_CACHE ())];
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
/*if MMU disabled or C flag is set alloc cache */
|
||||
if (MMU_Disabled) {
|
||||
c = 1;
|
||||
pa = va;
|
||||
}
|
||||
else {
|
||||
c = tlb_c_flag (tlb);
|
||||
pa = tlb_va_to_pa (tlb, va);
|
||||
}
|
||||
|
||||
if (c) {
|
||||
int index;
|
||||
|
||||
debug_count++;
|
||||
cache = mmu_cache_alloc (state, I_CACHE (), va, pa);
|
||||
index = va_cache_index (va, I_CACHE ());
|
||||
*instr = cache->data[va_cache_index (va, I_CACHE ())];
|
||||
}
|
||||
else
|
||||
//*instr = mem_read_word (state, pa);
|
||||
bus_read(32, pa, instr);
|
||||
|
||||
return NO_FAULT;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static fault_t
|
||||
sa_mmu_read_byte (ARMul_State * state, ARMword virt_addr, ARMword * data)
|
||||
{
|
||||
//ARMword temp,offset;
|
||||
fault_t fault;
|
||||
fault = sa_mmu_read (state, virt_addr, data, ARM_BYTE_TYPE);
|
||||
return fault;
|
||||
}
|
||||
|
||||
static fault_t
|
||||
sa_mmu_read_halfword (ARMul_State * state, ARMword virt_addr, ARMword * data)
|
||||
{
|
||||
//ARMword temp,offset;
|
||||
fault_t fault;
|
||||
fault = sa_mmu_read (state, virt_addr, data, ARM_HALFWORD_TYPE);
|
||||
return fault;
|
||||
}
|
||||
|
||||
static fault_t
|
||||
sa_mmu_read_word (ARMul_State * state, ARMword virt_addr, ARMword * data)
|
||||
{
|
||||
return sa_mmu_read (state, virt_addr, data, ARM_WORD_TYPE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
static fault_t
|
||||
sa_mmu_read (ARMul_State * state, ARMword va, ARMword * data,
|
||||
ARMword datatype)
|
||||
{
|
||||
fault_t fault;
|
||||
rb_entry_t *rb;
|
||||
tlb_entry_t *tlb;
|
||||
cache_line_t *cache;
|
||||
ARMword pa, real_va, temp, offset;
|
||||
|
||||
DEBUG_LOG(ARM11, "va = %x\n", va);
|
||||
|
||||
va = mmu_pid_va_map (va);
|
||||
real_va = va;
|
||||
/*if MMU disabled, memory_read */
|
||||
if (MMU_Disabled) {
|
||||
//*data = mem_read_word(state, va);
|
||||
if (datatype == ARM_BYTE_TYPE)
|
||||
//*data = mem_read_byte (state, va);
|
||||
bus_read(8, va, data);
|
||||
else if (datatype == ARM_HALFWORD_TYPE)
|
||||
//*data = mem_read_halfword (state, va);
|
||||
bus_read(16, va, data);
|
||||
else if (datatype == ARM_WORD_TYPE)
|
||||
//*data = mem_read_word (state, va);
|
||||
bus_read(32, va, data);
|
||||
else {
|
||||
printf ("SKYEYE:1 sa_mmu_read error: unknown data type %d\n", datatype);
|
||||
// skyeye_exit (-1);
|
||||
}
|
||||
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
/*align check */
|
||||
if (((va & 3) && (datatype == ARM_WORD_TYPE) && MMU_Aligned) ||
|
||||
((va & 1) && (datatype == ARM_HALFWORD_TYPE) && MMU_Aligned)) {
|
||||
DEBUG_LOG(ARM11, "align\n");
|
||||
return ALIGNMENT_FAULT;
|
||||
} // else
|
||||
|
||||
va &= ~(WORD_SIZE - 1);
|
||||
|
||||
/*translate va to tlb */
|
||||
fault = translate (state, va, D_TLB (), &tlb);
|
||||
if (fault) {
|
||||
DEBUG_LOG(ARM11, "translate\n");
|
||||
return fault;
|
||||
}
|
||||
/*check access permission */
|
||||
fault = check_access (state, va, tlb, 1);
|
||||
if (fault)
|
||||
return fault;
|
||||
/*search in read buffer */
|
||||
rb = mmu_rb_search (RB (), va);
|
||||
if (rb) {
|
||||
if (rb->fault)
|
||||
return rb->fault;
|
||||
*data = rb->data[(va & (rb_masks[rb->type] - 1)) >> WORD_SHT];
|
||||
goto datatrans;
|
||||
//return 0;
|
||||
};
|
||||
/*search main cache */
|
||||
cache = mmu_cache_search (state, MAIN_D_CACHE (), va);
|
||||
if (cache) {
|
||||
*data = cache->data[va_cache_index (va, MAIN_D_CACHE ())];
|
||||
goto datatrans;
|
||||
//return 0;
|
||||
}
|
||||
/*search mini cache */
|
||||
cache = mmu_cache_search (state, MINI_D_CACHE (), va);
|
||||
if (cache) {
|
||||
*data = cache->data[va_cache_index (va, MINI_D_CACHE ())];
|
||||
goto datatrans;
|
||||
//return 0;
|
||||
}
|
||||
|
||||
/*get phy_addr */
|
||||
pa = tlb_va_to_pa (tlb, va);
|
||||
if ((pa >= 0xe0000000) && (pa < 0xe8000000)) {
|
||||
if (tlb_c_flag (tlb)) {
|
||||
if (tlb_b_flag (tlb)) {
|
||||
mmu_cache_soft_flush (state, MAIN_D_CACHE (),
|
||||
pa);
|
||||
}
|
||||
else {
|
||||
mmu_cache_soft_flush (state, MINI_D_CACHE (),
|
||||
pa);
|
||||
}
|
||||
}
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
/*if Buffer, drain Write Buffer first */
|
||||
if (tlb_b_flag (tlb))
|
||||
mmu_wb_drain_all (state, WB ());
|
||||
|
||||
/*alloc cache or mem_read */
|
||||
if (tlb_c_flag (tlb) && MMU_DCacheEnabled) {
|
||||
cache_s *cache_t;
|
||||
|
||||
if (tlb_b_flag (tlb))
|
||||
cache_t = MAIN_D_CACHE ();
|
||||
else
|
||||
cache_t = MINI_D_CACHE ();
|
||||
cache = mmu_cache_alloc (state, cache_t, va, pa);
|
||||
*data = cache->data[va_cache_index (va, cache_t)];
|
||||
}
|
||||
else {
|
||||
//*data = mem_read_word(state, pa);
|
||||
if (datatype == ARM_BYTE_TYPE)
|
||||
//*data = mem_read_byte (state, pa | (real_va & 3));
|
||||
bus_read(8, pa | (real_va & 3), data);
|
||||
else if (datatype == ARM_HALFWORD_TYPE)
|
||||
//*data = mem_read_halfword (state, pa | (real_va & 2));
|
||||
bus_read(16, pa | (real_va & 2), data);
|
||||
else if (datatype == ARM_WORD_TYPE)
|
||||
//*data = mem_read_word (state, pa);
|
||||
bus_read(32, pa, data);
|
||||
else {
|
||||
printf ("SKYEYE:2 sa_mmu_read error: unknown data type %d\n", datatype);
|
||||
// skyeye_exit (-1);
|
||||
}
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
|
||||
datatrans:
|
||||
if (datatype == ARM_HALFWORD_TYPE) {
|
||||
temp = *data;
|
||||
offset = (((ARMword) state->bigendSig * 2) ^ (real_va & 2)) << 3; /* bit offset into the word */
|
||||
*data = (temp >> offset) & 0xffff;
|
||||
}
|
||||
else if (datatype == ARM_BYTE_TYPE) {
|
||||
temp = *data;
|
||||
offset = (((ARMword) state->bigendSig * 3) ^ (real_va & 3)) << 3; /* bit offset into the word */
|
||||
*data = (temp >> offset & 0xffL);
|
||||
}
|
||||
end:
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
|
||||
static fault_t
|
||||
sa_mmu_write_byte (ARMul_State * state, ARMword virt_addr, ARMword data)
|
||||
{
|
||||
return sa_mmu_write (state, virt_addr, data, ARM_BYTE_TYPE);
|
||||
}
|
||||
|
||||
static fault_t
|
||||
sa_mmu_write_halfword (ARMul_State * state, ARMword virt_addr, ARMword data)
|
||||
{
|
||||
return sa_mmu_write (state, virt_addr, data, ARM_HALFWORD_TYPE);
|
||||
}
|
||||
|
||||
static fault_t
|
||||
sa_mmu_write_word (ARMul_State * state, ARMword virt_addr, ARMword data)
|
||||
{
|
||||
return sa_mmu_write (state, virt_addr, data, ARM_WORD_TYPE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static fault_t
|
||||
sa_mmu_write (ARMul_State * state, ARMword va, ARMword data, ARMword datatype)
|
||||
{
|
||||
tlb_entry_t *tlb;
|
||||
cache_line_t *cache;
|
||||
int b;
|
||||
ARMword pa, real_va;
|
||||
fault_t fault;
|
||||
|
||||
DEBUG_LOG(ARM11, "va = %x, val = %x\n", va, data);
|
||||
va = mmu_pid_va_map (va);
|
||||
real_va = va;
|
||||
|
||||
/*search instruction cache */
|
||||
cache = mmu_cache_search (state, I_CACHE (), va);
|
||||
if (cache) {
|
||||
update_cache (state, va, data, datatype, cache, I_CACHE (),
|
||||
real_va);
|
||||
}
|
||||
|
||||
if (MMU_Disabled) {
|
||||
//mem_write_word(state, va, data);
|
||||
if (datatype == ARM_BYTE_TYPE)
|
||||
//mem_write_byte (state, va, data);
|
||||
bus_write(8, va, data);
|
||||
else if (datatype == ARM_HALFWORD_TYPE)
|
||||
//mem_write_halfword (state, va, data);
|
||||
bus_write(16, va, data);
|
||||
else if (datatype == ARM_WORD_TYPE)
|
||||
//mem_write_word (state, va, data);
|
||||
bus_write(32, va, data);
|
||||
else {
|
||||
printf ("SKYEYE:1 sa_mmu_write error: unknown data type %d\n", datatype);
|
||||
// skyeye_exit (-1);
|
||||
}
|
||||
|
||||
return NO_FAULT;
|
||||
}
|
||||
/*align check */
|
||||
//if ((va & (WORD_SIZE - 1)) && MMU_Aligned){
|
||||
if (((va & 3) && (datatype == ARM_WORD_TYPE) && MMU_Aligned) ||
|
||||
((va & 1) && (datatype == ARM_HALFWORD_TYPE) && MMU_Aligned)) {
|
||||
DEBUG_LOG(ARM11, "align\n");
|
||||
return ALIGNMENT_FAULT;
|
||||
} //else
|
||||
va &= ~(WORD_SIZE - 1);
|
||||
/*tlb translate */
|
||||
fault = translate (state, va, D_TLB (), &tlb);
|
||||
if (fault) {
|
||||
DEBUG_LOG(ARM11, "translate\n");
|
||||
return fault;
|
||||
}
|
||||
/*tlb check access */
|
||||
fault = check_access (state, va, tlb, 0);
|
||||
if (fault) {
|
||||
DEBUG_LOG(ARM11, "check_access\n");
|
||||
return fault;
|
||||
}
|
||||
/*search main cache */
|
||||
cache = mmu_cache_search (state, MAIN_D_CACHE (), va);
|
||||
if (cache) {
|
||||
update_cache (state, va, data, datatype, cache,
|
||||
MAIN_D_CACHE (), real_va);
|
||||
}
|
||||
else {
|
||||
/*search mini cache */
|
||||
cache = mmu_cache_search (state, MINI_D_CACHE (), va);
|
||||
if (cache) {
|
||||
update_cache (state, va, data, datatype, cache,
|
||||
MINI_D_CACHE (), real_va);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cache) {
|
||||
b = tlb_b_flag (tlb);
|
||||
pa = tlb_va_to_pa (tlb, va);
|
||||
if (b) {
|
||||
if (MMU_WBEnabled) {
|
||||
if (datatype == ARM_WORD_TYPE)
|
||||
mmu_wb_write_bytes (state, WB (), pa,
|
||||
(ARMbyte*)&data, 4);
|
||||
else if (datatype == ARM_HALFWORD_TYPE)
|
||||
mmu_wb_write_bytes (state, WB (),
|
||||
(pa |
|
||||
(real_va & 2)),
|
||||
(ARMbyte*)&data, 2);
|
||||
else if (datatype == ARM_BYTE_TYPE)
|
||||
mmu_wb_write_bytes (state, WB (),
|
||||
(pa |
|
||||
(real_va & 3)),
|
||||
(ARMbyte*)&data, 1);
|
||||
|
||||
}
|
||||
else {
|
||||
if (datatype == ARM_WORD_TYPE)
|
||||
//mem_write_word (state, pa, data);
|
||||
bus_write(32, pa, data);
|
||||
else if (datatype == ARM_HALFWORD_TYPE)
|
||||
/*
|
||||
mem_write_halfword (state,
|
||||
(pa |
|
||||
(real_va & 2)),
|
||||
data);
|
||||
*/
|
||||
bus_write(16, pa | (real_va & 2), data);
|
||||
else if (datatype == ARM_BYTE_TYPE)
|
||||
/*
|
||||
mem_write_byte (state,
|
||||
(pa | (real_va & 3)),
|
||||
data);
|
||||
*/
|
||||
bus_write(8, pa | (real_va & 3), data);
|
||||
}
|
||||
}
|
||||
else {
|
||||
mmu_wb_drain_all (state, WB ());
|
||||
|
||||
if (datatype == ARM_WORD_TYPE)
|
||||
//mem_write_word (state, pa, data);
|
||||
bus_write(32, pa, data);
|
||||
else if (datatype == ARM_HALFWORD_TYPE)
|
||||
/*
|
||||
mem_write_halfword (state,
|
||||
(pa | (real_va & 2)),
|
||||
data);
|
||||
*/
|
||||
bus_write(16, pa | (real_va & 2), data);
|
||||
else if (datatype == ARM_BYTE_TYPE)
|
||||
/*
|
||||
mem_write_byte (state, (pa | (real_va & 3)),
|
||||
data);
|
||||
*/
|
||||
bus_write(8, pa | (real_va & 3), data);
|
||||
}
|
||||
}
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
static fault_t
|
||||
update_cache (ARMul_State * state, ARMword va, ARMword data, ARMword datatype,
|
||||
cache_line_t * cache, cache_s * cache_t, ARMword real_va)
|
||||
{
|
||||
ARMword temp, offset;
|
||||
|
||||
ARMword index = va_cache_index (va, cache_t);
|
||||
|
||||
//cache->data[index] = data;
|
||||
|
||||
if (datatype == ARM_WORD_TYPE)
|
||||
cache->data[index] = data;
|
||||
else if (datatype == ARM_HALFWORD_TYPE) {
|
||||
temp = cache->data[index];
|
||||
offset = (((ARMword) state->bigendSig * 2) ^ (real_va & 2)) << 3; /* bit offset into the word */
|
||||
cache->data[index] =
|
||||
(temp & ~(0xffffL << offset)) | ((data & 0xffffL) <<
|
||||
offset);
|
||||
}
|
||||
else if (datatype == ARM_BYTE_TYPE) {
|
||||
temp = cache->data[index];
|
||||
offset = (((ARMword) state->bigendSig * 3) ^ (real_va & 3)) << 3; /* bit offset into the word */
|
||||
cache->data[index] =
|
||||
(temp & ~(0xffL << offset)) | ((data & 0xffL) <<
|
||||
offset);
|
||||
}
|
||||
|
||||
if (index < (cache_t->width >> (WORD_SHT + 1)))
|
||||
cache->tag |= TAG_FIRST_HALF_DIRTY;
|
||||
else
|
||||
cache->tag |= TAG_LAST_HALF_DIRTY;
|
||||
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
ARMword
|
||||
sa_mmu_mrc (ARMul_State * state, ARMword instr, ARMword * value)
|
||||
{
|
||||
mmu_regnum_t creg = (mmu_regnum_t)(BITS (16, 19) & 15);
|
||||
ARMword data;
|
||||
|
||||
switch (creg) {
|
||||
case MMU_ID:
|
||||
// printf("mmu_mrc read ID ");
|
||||
data = 0x41007100; /* v3 */
|
||||
data = state->cpu->cpu_val;
|
||||
break;
|
||||
case MMU_CONTROL:
|
||||
// printf("mmu_mrc read CONTROL");
|
||||
data = state->mmu.control;
|
||||
break;
|
||||
case MMU_TRANSLATION_TABLE_BASE:
|
||||
// printf("mmu_mrc read TTB ");
|
||||
data = state->mmu.translation_table_base;
|
||||
break;
|
||||
case MMU_DOMAIN_ACCESS_CONTROL:
|
||||
// printf("mmu_mrc read DACR ");
|
||||
data = state->mmu.domain_access_control;
|
||||
break;
|
||||
case MMU_FAULT_STATUS:
|
||||
// printf("mmu_mrc read FSR ");
|
||||
data = state->mmu.fault_status;
|
||||
break;
|
||||
case MMU_FAULT_ADDRESS:
|
||||
// printf("mmu_mrc read FAR ");
|
||||
data = state->mmu.fault_address;
|
||||
break;
|
||||
case MMU_PID:
|
||||
data = state->mmu.process_id;
|
||||
default:
|
||||
printf ("mmu_mrc read UNKNOWN - reg %d\n", creg);
|
||||
data = 0;
|
||||
break;
|
||||
}
|
||||
// printf("\t\t\t\t\tpc = 0x%08x\n", state->Reg[15]);
|
||||
*value = data;
|
||||
return data;
|
||||
}
|
||||
|
||||
void
|
||||
sa_mmu_cache_ops (ARMul_State * state, ARMword instr, ARMword value)
|
||||
{
|
||||
int CRm, OPC_2;
|
||||
|
||||
CRm = BITS (0, 3);
|
||||
OPC_2 = BITS (5, 7);
|
||||
|
||||
if (OPC_2 == 0 && CRm == 7) {
|
||||
mmu_cache_invalidate_all (state, I_CACHE ());
|
||||
mmu_cache_invalidate_all (state, MAIN_D_CACHE ());
|
||||
mmu_cache_invalidate_all (state, MINI_D_CACHE ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 0 && CRm == 5) {
|
||||
mmu_cache_invalidate_all (state, I_CACHE ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 0 && CRm == 6) {
|
||||
mmu_cache_invalidate_all (state, MAIN_D_CACHE ());
|
||||
mmu_cache_invalidate_all (state, MINI_D_CACHE ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 1 && CRm == 6) {
|
||||
mmu_cache_invalidate (state, MAIN_D_CACHE (), value);
|
||||
mmu_cache_invalidate (state, MINI_D_CACHE (), value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 1 && CRm == 0xa) {
|
||||
mmu_cache_clean (state, MAIN_D_CACHE (), value);
|
||||
mmu_cache_clean (state, MINI_D_CACHE (), value);
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 4 && CRm == 0xa) {
|
||||
mmu_wb_drain_all (state, WB ());
|
||||
return;
|
||||
}
|
||||
ERROR_LOG(ARM11, "Unknow OPC_2 = %x CRm = %x\n", OPC_2, CRm);
|
||||
}
|
||||
|
||||
static void
|
||||
sa_mmu_tlb_ops (ARMul_State * state, ARMword instr, ARMword value)
|
||||
{
|
||||
int CRm, OPC_2;
|
||||
|
||||
CRm = BITS (0, 3);
|
||||
OPC_2 = BITS (5, 7);
|
||||
|
||||
|
||||
if (OPC_2 == 0 && CRm == 0x7) {
|
||||
mmu_tlb_invalidate_all (state, I_TLB ());
|
||||
mmu_tlb_invalidate_all (state, D_TLB ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 0 && CRm == 0x5) {
|
||||
mmu_tlb_invalidate_all (state, I_TLB ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 0 && CRm == 0x6) {
|
||||
mmu_tlb_invalidate_all (state, D_TLB ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 1 && CRm == 0x6) {
|
||||
mmu_tlb_invalidate_entry (state, D_TLB (), value);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR_LOG(ARM11, "Unknow OPC_2 = %x CRm = %x\n", OPC_2, CRm);
|
||||
}
|
||||
|
||||
static void
|
||||
sa_mmu_rb_ops (ARMul_State * state, ARMword instr, ARMword value)
|
||||
{
|
||||
int CRm, OPC_2;
|
||||
|
||||
CRm = BITS (0, 3);
|
||||
OPC_2 = BITS (5, 7);
|
||||
|
||||
if (OPC_2 == 0x0 && CRm == 0x0) {
|
||||
mmu_rb_invalidate_all (RB ());
|
||||
return;
|
||||
}
|
||||
|
||||
if (OPC_2 == 0x2) {
|
||||
int idx = CRm & 0x3;
|
||||
int type = ((CRm >> 2) & 0x3) + 1;
|
||||
|
||||
if ((idx < 4) && (type < 4))
|
||||
mmu_rb_load (state, RB (), idx, type, value);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((OPC_2 == 1) && (CRm < 4)) {
|
||||
mmu_rb_invalidate_entry (RB (), CRm);
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR_LOG(ARM11, "Unknow OPC_2 = %x CRm = %x\n", OPC_2, CRm);
|
||||
}
|
||||
|
||||
static ARMword
|
||||
sa_mmu_mcr (ARMul_State * state, ARMword instr, ARMword value)
|
||||
{
|
||||
mmu_regnum_t creg = (mmu_regnum_t)(BITS (16, 19) & 15);
|
||||
if (!strncmp (state->cpu->cpu_arch_name, "armv4", 5)) {
|
||||
switch (creg) {
|
||||
case MMU_CONTROL:
|
||||
// printf("mmu_mcr wrote CONTROL ");
|
||||
state->mmu.control = (value | 0x70) & 0xFFFD;
|
||||
break;
|
||||
case MMU_TRANSLATION_TABLE_BASE:
|
||||
// printf("mmu_mcr wrote TTB ");
|
||||
state->mmu.translation_table_base =
|
||||
value & 0xFFFFC000;
|
||||
break;
|
||||
case MMU_DOMAIN_ACCESS_CONTROL:
|
||||
// printf("mmu_mcr wrote DACR ");
|
||||
state->mmu.domain_access_control = value;
|
||||
break;
|
||||
|
||||
case MMU_FAULT_STATUS:
|
||||
state->mmu.fault_status = value & 0xFF;
|
||||
break;
|
||||
case MMU_FAULT_ADDRESS:
|
||||
state->mmu.fault_address = value;
|
||||
break;
|
||||
|
||||
case MMU_CACHE_OPS:
|
||||
sa_mmu_cache_ops (state, instr, value);
|
||||
break;
|
||||
case MMU_TLB_OPS:
|
||||
sa_mmu_tlb_ops (state, instr, value);
|
||||
break;
|
||||
case MMU_SA_RB_OPS:
|
||||
sa_mmu_rb_ops (state, instr, value);
|
||||
break;
|
||||
case MMU_SA_DEBUG:
|
||||
break;
|
||||
case MMU_SA_CP15_R15:
|
||||
break;
|
||||
case MMU_PID:
|
||||
//2004-06-06 lyh, bug provided by wen ye wenye@cs.ucsb.edu
|
||||
state->mmu.process_id = value & 0x7e000000;
|
||||
break;
|
||||
|
||||
default:
|
||||
printf ("mmu_mcr wrote UNKNOWN - reg %d\n", creg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//teawater add for arm2x86 2005.06.24-------------------------------------------
|
||||
static int
|
||||
sa_mmu_v2p_dbct (ARMul_State * state, ARMword virt_addr, ARMword * phys_addr)
|
||||
{
|
||||
fault_t fault;
|
||||
tlb_entry_t *tlb;
|
||||
|
||||
virt_addr = mmu_pid_va_map (virt_addr);
|
||||
if (MMU_Enabled) {
|
||||
|
||||
/*align check */
|
||||
if ((virt_addr & (WORD_SIZE - 1)) && MMU_Aligned) {
|
||||
DEBUG_LOG(ARM11, "align\n");
|
||||
return ALIGNMENT_FAULT;
|
||||
}
|
||||
else
|
||||
virt_addr &= ~(WORD_SIZE - 1);
|
||||
|
||||
/*translate tlb */
|
||||
fault = translate (state, virt_addr, I_TLB (), &tlb);
|
||||
if (fault) {
|
||||
DEBUG_LOG(ARM11, "translate\n");
|
||||
return fault;
|
||||
}
|
||||
|
||||
/*check access */
|
||||
fault = check_access (state, virt_addr, tlb, 1);
|
||||
if (fault) {
|
||||
DEBUG_LOG(ARM11, "check_fault\n");
|
||||
return fault;
|
||||
}
|
||||
}
|
||||
|
||||
if (MMU_Disabled) {
|
||||
*phys_addr = virt_addr;
|
||||
}
|
||||
else {
|
||||
*phys_addr = tlb_va_to_pa (tlb, virt_addr);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
//AJ2D--------------------------------------------------------------------------
|
||||
|
||||
/*sa mmu_ops_t*/
|
||||
mmu_ops_t sa_mmu_ops = {
|
||||
sa_mmu_init,
|
||||
sa_mmu_exit,
|
||||
sa_mmu_read_byte,
|
||||
sa_mmu_write_byte,
|
||||
sa_mmu_read_halfword,
|
||||
sa_mmu_write_halfword,
|
||||
sa_mmu_read_word,
|
||||
sa_mmu_write_word,
|
||||
sa_mmu_load_instr,
|
||||
sa_mmu_mcr,
|
||||
sa_mmu_mrc,
|
||||
//teawater add for arm2x86 2005.06.24-------------------------------------------
|
||||
sa_mmu_v2p_dbct,
|
||||
//AJ2D--------------------------------------------------------------------------
|
||||
};
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
sa_mmu.h - StrongARM Memory Management Unit emulation.
|
||||
ARMulator extensions for SkyEye.
|
||||
<lyhost@263.net>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _SA_MMU_H_
|
||||
#define _SA_MMU_H_
|
||||
|
||||
|
||||
/**
|
||||
* The interface of read data from bus
|
||||
*/
|
||||
int bus_read(short size, int addr, uint32_t * value);
|
||||
|
||||
/**
|
||||
* The interface of write data from bus
|
||||
*/
|
||||
int bus_write(short size, int addr, uint32_t value);
|
||||
|
||||
|
||||
typedef struct sa_mmu_s
|
||||
{
|
||||
tlb_s i_tlb;
|
||||
cache_s i_cache;
|
||||
|
||||
tlb_s d_tlb;
|
||||
cache_s main_d_cache;
|
||||
cache_s mini_d_cache;
|
||||
rb_s rb_t;
|
||||
wb_s wb_t;
|
||||
} sa_mmu_t;
|
||||
|
||||
#define I_TLB() (&state->mmu.u.sa_mmu.i_tlb)
|
||||
#define I_CACHE() (&state->mmu.u.sa_mmu.i_cache)
|
||||
|
||||
#define D_TLB() (&state->mmu.u.sa_mmu.d_tlb)
|
||||
#define MAIN_D_CACHE() (&state->mmu.u.sa_mmu.main_d_cache)
|
||||
#define MINI_D_CACHE() (&state->mmu.u.sa_mmu.mini_d_cache)
|
||||
#define WB() (&state->mmu.u.sa_mmu.wb_t)
|
||||
#define RB() (&state->mmu.u.sa_mmu.rb_t)
|
||||
|
||||
extern mmu_ops_t sa_mmu_ops;
|
||||
#endif /*_SA_MMU_H_*/
|
@ -0,0 +1,307 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
|
||||
ARMword tlb_masks[] = {
|
||||
0x00000000, /* TLB_INVALID */
|
||||
0xFFFFF000, /* TLB_SMALLPAGE */
|
||||
0xFFFF0000, /* TLB_LARGEPAGE */
|
||||
0xFFF00000, /* TLB_SECTION */
|
||||
0xFFFFF000, /*TLB_ESMALLPAGE, have TEX attirbute, only for XScale */
|
||||
0xFFFFFC00 /* TLB_TINYPAGE */
|
||||
};
|
||||
|
||||
/* This function encodes table 8-2 Interpreting AP bits,
|
||||
returning non-zero if access is allowed. */
|
||||
static int
|
||||
check_perms (ARMul_State * state, int ap, int read)
|
||||
{
|
||||
int s, r, user;
|
||||
|
||||
s = state->mmu.control & CONTROL_SYSTEM;
|
||||
r = state->mmu.control & CONTROL_ROM;
|
||||
//chy 2006-02-15 , should consider system mode, don't conside 26bit mode
|
||||
user = (state->Mode == USER32MODE) || (state->Mode == USER26MODE) || (state->Mode == SYSTEM32MODE);
|
||||
|
||||
switch (ap) {
|
||||
case 0:
|
||||
return read && ((s && !user) || r);
|
||||
case 1:
|
||||
return !user;
|
||||
case 2:
|
||||
return read || !user;
|
||||
case 3:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fault_t
|
||||
check_access (ARMul_State * state, ARMword virt_addr, tlb_entry_t * tlb,
|
||||
int read)
|
||||
{
|
||||
int access;
|
||||
|
||||
state->mmu.last_domain = tlb->domain;
|
||||
access = (state->mmu.domain_access_control >> (tlb->domain * 2)) & 3;
|
||||
if ((access == 0) || (access == 2)) {
|
||||
/* It's unclear from the documentation whether this
|
||||
should always raise a section domain fault, or if
|
||||
it should be a page domain fault in the case of an
|
||||
L1 that describes a page table. In the ARM710T
|
||||
datasheets, "Figure 8-9: Sequence for checking faults"
|
||||
seems to indicate the former, while "Table 8-4: Priority
|
||||
encoding of fault status" gives a value for FS[3210] in
|
||||
the event of a domain fault for a page. Hmm. */
|
||||
return SECTION_DOMAIN_FAULT;
|
||||
}
|
||||
if (access == 1) {
|
||||
/* client access - check perms */
|
||||
int subpage, ap;
|
||||
|
||||
switch (tlb->mapping) {
|
||||
/*ks 2004-05-09
|
||||
* only for XScale
|
||||
* Extend Small Page(ESP) Format
|
||||
* 31-12 bits the base addr of ESP
|
||||
* 11-10 bits SBZ
|
||||
* 9-6 bits TEX
|
||||
* 5-4 bits AP
|
||||
* 3 bit C
|
||||
* 2 bit B
|
||||
* 1-0 bits 11
|
||||
* */
|
||||
case TLB_ESMALLPAGE: //xj
|
||||
subpage = 0;
|
||||
//printf("TLB_ESMALLPAGE virt_addr=0x%x \n",virt_addr );
|
||||
break;
|
||||
|
||||
case TLB_TINYPAGE:
|
||||
subpage = 0;
|
||||
//printf("TLB_TINYPAGE virt_addr=0x%x \n",virt_addr );
|
||||
break;
|
||||
|
||||
case TLB_SMALLPAGE:
|
||||
subpage = (virt_addr >> 10) & 3;
|
||||
break;
|
||||
case TLB_LARGEPAGE:
|
||||
subpage = (virt_addr >> 14) & 3;
|
||||
break;
|
||||
case TLB_SECTION:
|
||||
subpage = 3;
|
||||
break;
|
||||
default:
|
||||
assert (0);
|
||||
subpage = 0; /* cleans a warning */
|
||||
}
|
||||
ap = (tlb->perms >> (subpage * 2 + 4)) & 3;
|
||||
if (!check_perms (state, ap, read)) {
|
||||
if (tlb->mapping == TLB_SECTION) {
|
||||
return SECTION_PERMISSION_FAULT;
|
||||
}
|
||||
else {
|
||||
return SUBPAGE_PERMISSION_FAULT;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { /* access == 3 */
|
||||
/* manager access - don't check perms */
|
||||
}
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
fault_t
|
||||
translate (ARMul_State * state, ARMword virt_addr, tlb_s * tlb_t,
|
||||
tlb_entry_t ** tlb)
|
||||
{
|
||||
*tlb = mmu_tlb_search (state, tlb_t, virt_addr);
|
||||
if (!*tlb) {
|
||||
/* walk the translation tables */
|
||||
ARMword l1addr, l1desc;
|
||||
tlb_entry_t entry;
|
||||
|
||||
l1addr = state->mmu.translation_table_base & 0xFFFFC000;
|
||||
l1addr = (l1addr | (virt_addr >> 18)) & ~3;
|
||||
//l1desc = mem_read_word (state, l1addr);
|
||||
bus_read(32, l1addr, &l1desc);
|
||||
switch (l1desc & 3) {
|
||||
case 0:
|
||||
/*
|
||||
* according to Figure 3-9 Sequence for checking faults in arm manual,
|
||||
* section translation fault should be returned here.
|
||||
*/
|
||||
{
|
||||
return SECTION_TRANSLATION_FAULT;
|
||||
}
|
||||
case 3:
|
||||
/* fine page table */
|
||||
// dcl 2006-01-08
|
||||
{
|
||||
ARMword l2addr, l2desc;
|
||||
|
||||
l2addr = l1desc & 0xFFFFF000;
|
||||
l2addr = (l2addr |
|
||||
((virt_addr & 0x000FFC00) >> 8)) &
|
||||
~3;
|
||||
//l2desc = mem_read_word (state, l2addr);
|
||||
bus_read(32, l2addr, &l2desc);
|
||||
|
||||
entry.virt_addr = virt_addr;
|
||||
entry.phys_addr = l2desc;
|
||||
entry.perms = l2desc & 0x00000FFC;
|
||||
entry.domain = (l1desc >> 5) & 0x0000000F;
|
||||
switch (l2desc & 3) {
|
||||
case 0:
|
||||
state->mmu.last_domain = entry.domain;
|
||||
return PAGE_TRANSLATION_FAULT;
|
||||
case 3:
|
||||
entry.mapping = TLB_TINYPAGE;
|
||||
break;
|
||||
case 1:
|
||||
// this is untested
|
||||
entry.mapping = TLB_LARGEPAGE;
|
||||
break;
|
||||
case 2:
|
||||
// this is untested
|
||||
entry.mapping = TLB_SMALLPAGE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
/* coarse page table */
|
||||
{
|
||||
ARMword l2addr, l2desc;
|
||||
|
||||
l2addr = l1desc & 0xFFFFFC00;
|
||||
l2addr = (l2addr |
|
||||
((virt_addr & 0x000FF000) >> 10)) &
|
||||
~3;
|
||||
//l2desc = mem_read_word (state, l2addr);
|
||||
bus_read(32, l2addr, &l2desc);
|
||||
|
||||
entry.virt_addr = virt_addr;
|
||||
entry.phys_addr = l2desc;
|
||||
entry.perms = l2desc & 0x00000FFC;
|
||||
entry.domain = (l1desc >> 5) & 0x0000000F;
|
||||
//printf("SKYEYE:PAGE virt_addr = %x,l1desc=%x,phys_addr=%x\n",virt_addr,l1desc,entry.phys_addr);
|
||||
//chy 2003-09-02 for xscale
|
||||
switch (l2desc & 3) {
|
||||
case 0:
|
||||
state->mmu.last_domain = entry.domain;
|
||||
return PAGE_TRANSLATION_FAULT;
|
||||
case 3:
|
||||
if (!state->is_XScale) {
|
||||
state->mmu.last_domain =
|
||||
entry.domain;
|
||||
return PAGE_TRANSLATION_FAULT;
|
||||
};
|
||||
//ks 2004-05-09 xscale shold use Extend Small Page
|
||||
//entry.mapping = TLB_SMALLPAGE;
|
||||
entry.mapping = TLB_ESMALLPAGE; //xj
|
||||
break;
|
||||
case 1:
|
||||
entry.mapping = TLB_LARGEPAGE;
|
||||
break;
|
||||
case 2:
|
||||
entry.mapping = TLB_SMALLPAGE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
/* section */
|
||||
//printf("SKYEYE:WARNING: not implement section mapping incompletely\n");
|
||||
//printf("SKYEYE:SECTION virt_addr = %x,l1desc=%x\n",virt_addr,l1desc);
|
||||
//return SECTION_DOMAIN_FAULT;
|
||||
//#if 0
|
||||
entry.virt_addr = virt_addr;
|
||||
entry.phys_addr = l1desc;
|
||||
entry.perms = l1desc & 0x00000C0C;
|
||||
entry.domain = (l1desc >> 5) & 0x0000000F;
|
||||
entry.mapping = TLB_SECTION;
|
||||
break;
|
||||
//#endif
|
||||
}
|
||||
entry.virt_addr &= tlb_masks[entry.mapping];
|
||||
entry.phys_addr &= tlb_masks[entry.mapping];
|
||||
|
||||
/* place entry in the tlb */
|
||||
*tlb = &tlb_t->entrys[tlb_t->cycle];
|
||||
tlb_t->cycle = (tlb_t->cycle + 1) % tlb_t->num;
|
||||
**tlb = entry;
|
||||
}
|
||||
state->mmu.last_domain = (*tlb)->domain;
|
||||
return NO_FAULT;
|
||||
}
|
||||
|
||||
int
|
||||
mmu_tlb_init (tlb_s * tlb_t, int num)
|
||||
{
|
||||
tlb_entry_t *e;
|
||||
int i;
|
||||
|
||||
e = (tlb_entry_t *) malloc (sizeof (*e) * num);
|
||||
if (e == NULL) {
|
||||
ERROR_LOG(ARM11, "malloc size %d\n", sizeof (*e) * num);
|
||||
goto tlb_malloc_error;
|
||||
}
|
||||
tlb_t->entrys = e;
|
||||
for (i = 0; i < num; i++, e++)
|
||||
e->mapping = TLB_INVALID;
|
||||
tlb_t->cycle = 0;
|
||||
tlb_t->num = num;
|
||||
return 0;
|
||||
|
||||
tlb_malloc_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
mmu_tlb_exit (tlb_s * tlb_t)
|
||||
{
|
||||
free (tlb_t->entrys);
|
||||
};
|
||||
|
||||
void
|
||||
mmu_tlb_invalidate_all (ARMul_State * state, tlb_s * tlb_t)
|
||||
{
|
||||
int entry;
|
||||
|
||||
for (entry = 0; entry < tlb_t->num; entry++) {
|
||||
tlb_t->entrys[entry].mapping = TLB_INVALID;
|
||||
}
|
||||
tlb_t->cycle = 0;
|
||||
}
|
||||
|
||||
void
|
||||
mmu_tlb_invalidate_entry (ARMul_State * state, tlb_s * tlb_t, ARMword addr)
|
||||
{
|
||||
tlb_entry_t *tlb;
|
||||
|
||||
tlb = mmu_tlb_search (state, tlb_t, addr);
|
||||
if (tlb) {
|
||||
tlb->mapping = TLB_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
tlb_entry_t *
|
||||
mmu_tlb_search (ARMul_State * state, tlb_s * tlb_t, ARMword virt_addr)
|
||||
{
|
||||
int entry;
|
||||
|
||||
for (entry = 0; entry < tlb_t->num; entry++) {
|
||||
tlb_entry_t *tlb;
|
||||
ARMword mask;
|
||||
|
||||
tlb = &(tlb_t->entrys[entry]);
|
||||
if (tlb->mapping == TLB_INVALID) {
|
||||
continue;
|
||||
}
|
||||
mask = tlb_masks[tlb->mapping];
|
||||
if ((virt_addr & mask) == (tlb->virt_addr & mask)) {
|
||||
return tlb;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
@ -0,0 +1,149 @@
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
|
||||
/* wb_init
|
||||
* @wb_t :wb_t to init
|
||||
* @num :num of entrys
|
||||
* @nb :num of byte of each entry
|
||||
*
|
||||
* $ -1:error
|
||||
* 0:ok
|
||||
* */
|
||||
int
|
||||
mmu_wb_init (wb_s * wb_t, int num, int nb)
|
||||
{
|
||||
int i;
|
||||
wb_entry_t *entrys, *wb;
|
||||
|
||||
entrys = (wb_entry_t *) malloc (sizeof (*entrys) * num);
|
||||
if (entrys == NULL) {
|
||||
ERROR_LOG(ARM11, "malloc size %d\n", sizeof (*entrys) * num);
|
||||
goto entrys_malloc_error;
|
||||
}
|
||||
|
||||
for (wb = entrys, i = 0; i < num; i++, wb++) {
|
||||
/*chy 2004-06-06, fix bug found by wenye@cs.ucsb.edu */
|
||||
//wb->data = (ARMword *)malloc(sizeof(ARMword) * nb);
|
||||
wb->data = (ARMbyte *) malloc (nb);
|
||||
if (wb->data == NULL) {
|
||||
ERROR_LOG(ARM11, "malloc size of %d\n", nb);
|
||||
goto data_malloc_error;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
wb_t->first = wb_t->last = wb_t->used = 0;
|
||||
wb_t->num = num;
|
||||
wb_t->nb = nb;
|
||||
wb_t->entrys = entrys;
|
||||
return 0;
|
||||
|
||||
data_malloc_error:
|
||||
while (--i >= 0)
|
||||
free (entrys[i].data);
|
||||
free (entrys);
|
||||
entrys_malloc_error:
|
||||
return -1;
|
||||
};
|
||||
|
||||
/* wb_exit
|
||||
* @wb_t :wb_t to exit
|
||||
* */
|
||||
void
|
||||
mmu_wb_exit (wb_s * wb_t)
|
||||
{
|
||||
int i;
|
||||
wb_entry_t *wb;
|
||||
|
||||
wb = wb_t->entrys;
|
||||
for (i = 0; i < wb_t->num; i++, wb++) {
|
||||
free (wb->data);
|
||||
}
|
||||
free (wb_t->entrys);
|
||||
};
|
||||
|
||||
/* wb_write_words :put words in Write Buffer
|
||||
* @state: ARMul_State
|
||||
* @wb_t: write buffer
|
||||
* @pa: physical address
|
||||
* @data: data ptr
|
||||
* @n number of word to write
|
||||
*
|
||||
* Note: write buffer merge is not implemented, can be done late
|
||||
* */
|
||||
void
|
||||
mmu_wb_write_bytes (ARMul_State * state, wb_s * wb_t, ARMword pa,
|
||||
ARMbyte * data, int n)
|
||||
{
|
||||
int i;
|
||||
wb_entry_t *wb;
|
||||
|
||||
while (n) {
|
||||
if (wb_t->num == wb_t->used) {
|
||||
/*clean the last wb entry */
|
||||
ARMword t;
|
||||
|
||||
wb = &wb_t->entrys[wb_t->last];
|
||||
t = wb->pa;
|
||||
for (i = 0; i < wb->nb; i++) {
|
||||
//mem_write_byte (state, t, wb->data[i]);
|
||||
bus_write(8, t, wb->data[i]);
|
||||
//t += WORD_SIZE;
|
||||
t++;
|
||||
}
|
||||
wb_t->last++;
|
||||
if (wb_t->last == wb_t->num)
|
||||
wb_t->last = 0;
|
||||
wb_t->used--;
|
||||
}
|
||||
|
||||
wb = &wb_t->entrys[wb_t->first];
|
||||
i = (n < wb_t->nb) ? n : wb_t->nb;
|
||||
|
||||
wb->pa = pa;
|
||||
//pa += i << WORD_SHT;
|
||||
pa += i;
|
||||
|
||||
wb->nb = i;
|
||||
//memcpy(wb->data, data, i << WORD_SHT);
|
||||
memcpy (wb->data, data, i);
|
||||
data += i;
|
||||
n -= i;
|
||||
wb_t->first++;
|
||||
if (wb_t->first == wb_t->num)
|
||||
wb_t->first = 0;
|
||||
wb_t->used++;
|
||||
};
|
||||
//teawater add for set_dirty fflash cache function 2005.07.18-------------------
|
||||
#ifdef DBCT
|
||||
if (!skyeye_config.no_dbct) {
|
||||
tb_setdirty (state, pa, NULL);
|
||||
}
|
||||
#endif
|
||||
//AJ2D--------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
/* wb_drain_all
|
||||
* @wb_t wb_t to drain
|
||||
* */
|
||||
void
|
||||
mmu_wb_drain_all (ARMul_State * state, wb_s * wb_t)
|
||||
{
|
||||
ARMword pa;
|
||||
wb_entry_t *wb;
|
||||
int i;
|
||||
|
||||
while (wb_t->used) {
|
||||
wb = &wb_t->entrys[wb_t->last];
|
||||
pa = wb->pa;
|
||||
for (i = 0; i < wb->nb; i++) {
|
||||
//mem_write_byte (state, pa, wb->data[i]);
|
||||
bus_write(8, pa, wb->data[i]);
|
||||
//pa += WORD_SIZE;
|
||||
pa++;
|
||||
}
|
||||
wb_t->last++;
|
||||
if (wb_t->last == wb_t->num)
|
||||
wb_t->last = 0;
|
||||
wb_t->used--;
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* arch/arm/include/asm/vfp.h
|
||||
*
|
||||
* VFP register definitions.
|
||||
* First, the standard VFP set.
|
||||
*/
|
||||
|
||||
#define FPSID cr0
|
||||
#define FPSCR cr1
|
||||
#define MVFR1 cr6
|
||||
#define MVFR0 cr7
|
||||
#define FPEXC cr8
|
||||
#define FPINST cr9
|
||||
#define FPINST2 cr10
|
||||
|
||||
/* FPSID bits */
|
||||
#define FPSID_IMPLEMENTER_BIT (24)
|
||||
#define FPSID_IMPLEMENTER_MASK (0xff << FPSID_IMPLEMENTER_BIT)
|
||||
#define FPSID_SOFTWARE (1<<23)
|
||||
#define FPSID_FORMAT_BIT (21)
|
||||
#define FPSID_FORMAT_MASK (0x3 << FPSID_FORMAT_BIT)
|
||||
#define FPSID_NODOUBLE (1<<20)
|
||||
#define FPSID_ARCH_BIT (16)
|
||||
#define FPSID_ARCH_MASK (0xF << FPSID_ARCH_BIT)
|
||||
#define FPSID_PART_BIT (8)
|
||||
#define FPSID_PART_MASK (0xFF << FPSID_PART_BIT)
|
||||
#define FPSID_VARIANT_BIT (4)
|
||||
#define FPSID_VARIANT_MASK (0xF << FPSID_VARIANT_BIT)
|
||||
#define FPSID_REV_BIT (0)
|
||||
#define FPSID_REV_MASK (0xF << FPSID_REV_BIT)
|
||||
|
||||
/* FPEXC bits */
|
||||
#define FPEXC_EX (1 << 31)
|
||||
#define FPEXC_EN (1 << 30)
|
||||
#define FPEXC_DEX (1 << 29)
|
||||
#define FPEXC_FP2V (1 << 28)
|
||||
#define FPEXC_VV (1 << 27)
|
||||
#define FPEXC_TFV (1 << 26)
|
||||
#define FPEXC_LENGTH_BIT (8)
|
||||
#define FPEXC_LENGTH_MASK (7 << FPEXC_LENGTH_BIT)
|
||||
#define FPEXC_IDF (1 << 7)
|
||||
#define FPEXC_IXF (1 << 4)
|
||||
#define FPEXC_UFF (1 << 3)
|
||||
#define FPEXC_OFF (1 << 2)
|
||||
#define FPEXC_DZF (1 << 1)
|
||||
#define FPEXC_IOF (1 << 0)
|
||||
#define FPEXC_TRAP_MASK (FPEXC_IDF|FPEXC_IXF|FPEXC_UFF|FPEXC_OFF|FPEXC_DZF|FPEXC_IOF)
|
||||
|
||||
/* FPSCR bits */
|
||||
#define FPSCR_DEFAULT_NAN (1<<25)
|
||||
#define FPSCR_FLUSHTOZERO (1<<24)
|
||||
#define FPSCR_ROUND_NEAREST (0<<22)
|
||||
#define FPSCR_ROUND_PLUSINF (1<<22)
|
||||
#define FPSCR_ROUND_MINUSINF (2<<22)
|
||||
#define FPSCR_ROUND_TOZERO (3<<22)
|
||||
#define FPSCR_RMODE_BIT (22)
|
||||
#define FPSCR_RMODE_MASK (3 << FPSCR_RMODE_BIT)
|
||||
#define FPSCR_STRIDE_BIT (20)
|
||||
#define FPSCR_STRIDE_MASK (3 << FPSCR_STRIDE_BIT)
|
||||
#define FPSCR_LENGTH_BIT (16)
|
||||
#define FPSCR_LENGTH_MASK (7 << FPSCR_LENGTH_BIT)
|
||||
#define FPSCR_IOE (1<<8)
|
||||
#define FPSCR_DZE (1<<9)
|
||||
#define FPSCR_OFE (1<<10)
|
||||
#define FPSCR_UFE (1<<11)
|
||||
#define FPSCR_IXE (1<<12)
|
||||
#define FPSCR_IDE (1<<15)
|
||||
#define FPSCR_IOC (1<<0)
|
||||
#define FPSCR_DZC (1<<1)
|
||||
#define FPSCR_OFC (1<<2)
|
||||
#define FPSCR_UFC (1<<3)
|
||||
#define FPSCR_IXC (1<<4)
|
||||
#define FPSCR_IDC (1<<7)
|
||||
|
||||
/* MVFR0 bits */
|
||||
#define MVFR0_A_SIMD_BIT (0)
|
||||
#define MVFR0_A_SIMD_MASK (0xf << MVFR0_A_SIMD_BIT)
|
||||
|
||||
/* Bit patterns for decoding the packaged operation descriptors */
|
||||
#define VFPOPDESC_LENGTH_BIT (9)
|
||||
#define VFPOPDESC_LENGTH_MASK (0x07 << VFPOPDESC_LENGTH_BIT)
|
||||
#define VFPOPDESC_UNUSED_BIT (24)
|
||||
#define VFPOPDESC_UNUSED_MASK (0xFF << VFPOPDESC_UNUSED_BIT)
|
||||
#define VFPOPDESC_OPDESC_MASK (~(VFPOPDESC_LENGTH_MASK | VFPOPDESC_UNUSED_MASK))
|
@ -0,0 +1,357 @@
|
||||
/*
|
||||
armvfp.c - ARM VFPv3 emulation unit
|
||||
Copyright (C) 2003 Skyeye Develop Group
|
||||
for help please send mail to <skyeye-developer@lists.gro.clinux.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* Note: this file handles interface with arm core and vfp registers */
|
||||
|
||||
/* Opens debug for classic interpreter only */
|
||||
//#define DEBUG
|
||||
|
||||
#include "common/common.h"
|
||||
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
#include "core/arm/interpreter/vfp/vfp.h"
|
||||
|
||||
//ARMul_State* persistent_state; /* function calls from SoftFloat lib don't have an access to ARMul_state. */
|
||||
|
||||
unsigned
|
||||
VFPInit (ARMul_State *state)
|
||||
{
|
||||
state->VFP[VFP_OFFSET(VFP_FPSID)] = VFP_FPSID_IMPLMEN<<24 | VFP_FPSID_SW<<23 | VFP_FPSID_SUBARCH<<16 |
|
||||
VFP_FPSID_PARTNUM<<8 | VFP_FPSID_VARIANT<<4 | VFP_FPSID_REVISION;
|
||||
state->VFP[VFP_OFFSET(VFP_FPEXC)] = 0;
|
||||
state->VFP[VFP_OFFSET(VFP_FPSCR)] = 0;
|
||||
|
||||
//persistent_state = state;
|
||||
/* Reset only specify VFP_FPEXC_EN = '0' */
|
||||
|
||||
return No_exp;
|
||||
}
|
||||
|
||||
unsigned
|
||||
VFPMRC (ARMul_State * state, unsigned type, ARMword instr, ARMword * value)
|
||||
{
|
||||
/* MRC<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
|
||||
int CoProc = BITS (8, 11); /* 10 or 11 */
|
||||
int OPC_1 = BITS (21, 23);
|
||||
int Rt = BITS (12, 15);
|
||||
int CRn = BITS (16, 19);
|
||||
int CRm = BITS (0, 3);
|
||||
int OPC_2 = BITS (5, 7);
|
||||
|
||||
/* TODO check access permission */
|
||||
|
||||
/* CRn/opc1 CRm/opc2 */
|
||||
|
||||
if (CoProc == 10 || CoProc == 11)
|
||||
{
|
||||
#define VFP_MRC_TRANS
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MRC_TRANS
|
||||
}
|
||||
DEBUG_LOG(ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
|
||||
instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
unsigned
|
||||
VFPMCR (ARMul_State * state, unsigned type, ARMword instr, ARMword value)
|
||||
{
|
||||
/* MCR<c> <coproc>,<opc1>,<Rt>,<CRn>,<CRm>{,<opc2>} */
|
||||
int CoProc = BITS (8, 11); /* 10 or 11 */
|
||||
int OPC_1 = BITS (21, 23);
|
||||
int Rt = BITS (12, 15);
|
||||
int CRn = BITS (16, 19);
|
||||
int CRm = BITS (0, 3);
|
||||
int OPC_2 = BITS (5, 7);
|
||||
|
||||
/* TODO check access permission */
|
||||
|
||||
/* CRn/opc1 CRm/opc2 */
|
||||
if (CoProc == 10 || CoProc == 11)
|
||||
{
|
||||
#define VFP_MCR_TRANS
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MCR_TRANS
|
||||
}
|
||||
DEBUG_LOG(ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, CRn %x, CRm %x, OPC_2 %x\n",
|
||||
instr, CoProc, OPC_1, Rt, CRn, CRm, OPC_2);
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
unsigned
|
||||
VFPMRRC (ARMul_State * state, unsigned type, ARMword instr, ARMword * value1, ARMword * value2)
|
||||
{
|
||||
/* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
|
||||
int CoProc = BITS (8, 11); /* 10 or 11 */
|
||||
int OPC_1 = BITS (4, 7);
|
||||
int Rt = BITS (12, 15);
|
||||
int Rt2 = BITS (16, 19);
|
||||
int CRm = BITS (0, 3);
|
||||
|
||||
if (CoProc == 10 || CoProc == 11)
|
||||
{
|
||||
#define VFP_MRRC_TRANS
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MRRC_TRANS
|
||||
}
|
||||
DEBUG_LOG(ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
|
||||
instr, CoProc, OPC_1, Rt, Rt2, CRm);
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
unsigned
|
||||
VFPMCRR (ARMul_State * state, unsigned type, ARMword instr, ARMword value1, ARMword value2)
|
||||
{
|
||||
/* MCRR<c> <coproc>,<opc1>,<Rt>,<Rt2>,<CRm> */
|
||||
int CoProc = BITS (8, 11); /* 10 or 11 */
|
||||
int OPC_1 = BITS (4, 7);
|
||||
int Rt = BITS (12, 15);
|
||||
int Rt2 = BITS (16, 19);
|
||||
int CRm = BITS (0, 3);
|
||||
|
||||
/* TODO check access permission */
|
||||
|
||||
/* CRn/opc1 CRm/opc2 */
|
||||
|
||||
if (CoProc == 11 || CoProc == 10)
|
||||
{
|
||||
#define VFP_MCRR_TRANS
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MCRR_TRANS
|
||||
}
|
||||
DEBUG_LOG(ARM11, "Can't identify %x, CoProc %x, OPC_1 %x, Rt %x, Rt2 %x, CRm %x\n",
|
||||
instr, CoProc, OPC_1, Rt, Rt2, CRm);
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
unsigned
|
||||
VFPSTC (ARMul_State * state, unsigned type, ARMword instr, ARMword * value)
|
||||
{
|
||||
/* STC{L}<c> <coproc>,<CRd>,[<Rn>],<option> */
|
||||
int CoProc = BITS (8, 11); /* 10 or 11 */
|
||||
int CRd = BITS (12, 15);
|
||||
int Rn = BITS (16, 19);
|
||||
int imm8 = BITS (0, 7);
|
||||
int P = BIT(24);
|
||||
int U = BIT(23);
|
||||
int D = BIT(22);
|
||||
int W = BIT(21);
|
||||
|
||||
/* TODO check access permission */
|
||||
|
||||
/* VSTM */
|
||||
if ( (P|U|D|W) == 0 )
|
||||
{
|
||||
DEBUG_LOG(ARM11, "In %s, UNDEFINED\n", __FUNCTION__); exit(-1);
|
||||
}
|
||||
if (CoProc == 10 || CoProc == 11)
|
||||
{
|
||||
#if 1
|
||||
if (P == 0 && U == 0 && W == 0)
|
||||
{
|
||||
DEBUG_LOG(ARM11, "VSTM Related encodings\n"); exit(-1);
|
||||
}
|
||||
if (P == U && W == 1)
|
||||
{
|
||||
DEBUG_LOG(ARM11, "UNDEFINED\n"); exit(-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define VFP_STC_TRANS
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_STC_TRANS
|
||||
}
|
||||
DEBUG_LOG(ARM11, "Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n",
|
||||
instr, CoProc, CRd, Rn, imm8, P, U, D, W);
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
unsigned
|
||||
VFPLDC (ARMul_State * state, unsigned type, ARMword instr, ARMword value)
|
||||
{
|
||||
/* LDC{L}<c> <coproc>,<CRd>,[<Rn>] */
|
||||
int CoProc = BITS (8, 11); /* 10 or 11 */
|
||||
int CRd = BITS (12, 15);
|
||||
int Rn = BITS (16, 19);
|
||||
int imm8 = BITS (0, 7);
|
||||
int P = BIT(24);
|
||||
int U = BIT(23);
|
||||
int D = BIT(22);
|
||||
int W = BIT(21);
|
||||
|
||||
/* TODO check access permission */
|
||||
|
||||
if ( (P|U|D|W) == 0 )
|
||||
{
|
||||
DEBUG_LOG(ARM11, "In %s, UNDEFINED\n", __FUNCTION__); exit(-1);
|
||||
}
|
||||
if (CoProc == 10 || CoProc == 11)
|
||||
{
|
||||
#define VFP_LDC_TRANS
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_LDC_TRANS
|
||||
}
|
||||
DEBUG_LOG(ARM11, "Can't identify %x, CoProc %x, CRd %x, Rn %x, imm8 %x, P %x, U %x, D %x, W %x\n",
|
||||
instr, CoProc, CRd, Rn, imm8, P, U, D, W);
|
||||
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
unsigned
|
||||
VFPCDP (ARMul_State * state, unsigned type, ARMword instr)
|
||||
{
|
||||
/* CDP<c> <coproc>,<opc1>,<CRd>,<CRn>,<CRm>,<opc2> */
|
||||
int CoProc = BITS (8, 11); /* 10 or 11 */
|
||||
int OPC_1 = BITS (20, 23);
|
||||
int CRd = BITS (12, 15);
|
||||
int CRn = BITS (16, 19);
|
||||
int CRm = BITS (0, 3);
|
||||
int OPC_2 = BITS (5, 7);
|
||||
|
||||
/* TODO check access permission */
|
||||
|
||||
/* CRn/opc1 CRm/opc2 */
|
||||
|
||||
if (CoProc == 10 || CoProc == 11)
|
||||
{
|
||||
#define VFP_CDP_TRANS
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_CDP_TRANS
|
||||
|
||||
int exceptions = 0;
|
||||
if (CoProc == 10)
|
||||
exceptions = vfp_single_cpdo(state, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]);
|
||||
else
|
||||
exceptions = vfp_double_cpdo(state, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]);
|
||||
|
||||
vfp_raise_exceptions(state, exceptions, instr, state->VFP[VFP_OFFSET(VFP_FPSCR)]);
|
||||
|
||||
return ARMul_DONE;
|
||||
}
|
||||
DEBUG_LOG(ARM11, "Can't identify %x\n", instr);
|
||||
return ARMul_CANT;
|
||||
}
|
||||
|
||||
|
||||
/* ----------- MRC ------------ */
|
||||
#define VFP_MRC_IMPL
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MRC_IMPL
|
||||
|
||||
#define VFP_MRRC_IMPL
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MRRC_IMPL
|
||||
|
||||
|
||||
/* ----------- MCR ------------ */
|
||||
#define VFP_MCR_IMPL
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MCR_IMPL
|
||||
|
||||
#define VFP_MCRR_IMPL
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_MCRR_IMPL
|
||||
|
||||
/* Memory operation are not inlined, as old Interpreter and Fast interpreter
|
||||
don't have the same memory operation interface.
|
||||
Old interpreter framework does one access to coprocessor per data, and
|
||||
handles already data write, as well as address computation,
|
||||
which is not the case for Fast interpreter. Therefore, implementation
|
||||
of vfp instructions in old interpreter and fast interpreter are separate. */
|
||||
|
||||
/* ----------- STC ------------ */
|
||||
#define VFP_STC_IMPL
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_STC_IMPL
|
||||
|
||||
|
||||
/* ----------- LDC ------------ */
|
||||
#define VFP_LDC_IMPL
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_LDC_IMPL
|
||||
|
||||
|
||||
/* ----------- CDP ------------ */
|
||||
#define VFP_CDP_IMPL
|
||||
#include "core/arm/interpreter/vfp/vfpinstr.cpp"
|
||||
#undef VFP_CDP_IMPL
|
||||
|
||||
/* Miscellaneous functions */
|
||||
int32_t vfp_get_float(arm_core_t* state, unsigned int reg)
|
||||
{
|
||||
DBG("VFP get float: s%d=[%08x]\n", reg, state->ExtReg[reg]);
|
||||
return state->ExtReg[reg];
|
||||
}
|
||||
|
||||
void vfp_put_float(arm_core_t* state, int32_t val, unsigned int reg)
|
||||
{
|
||||
DBG("VFP put float: s%d <= [%08x]\n", reg, val);
|
||||
state->ExtReg[reg] = val;
|
||||
}
|
||||
|
||||
uint64_t vfp_get_double(arm_core_t* state, unsigned int reg)
|
||||
{
|
||||
uint64_t result;
|
||||
result = ((uint64_t) state->ExtReg[reg*2+1])<<32 | state->ExtReg[reg*2];
|
||||
DBG("VFP get double: s[%d-%d]=[%016llx]\n", reg*2+1, reg*2, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void vfp_put_double(arm_core_t* state, uint64_t val, unsigned int reg)
|
||||
{
|
||||
DBG("VFP put double: s[%d-%d] <= [%08x-%08x]\n", reg*2+1, reg*2, (uint32_t) (val>>32), (uint32_t) (val & 0xffffffff));
|
||||
state->ExtReg[reg*2] = (uint32_t) (val & 0xffffffff);
|
||||
state->ExtReg[reg*2+1] = (uint32_t) (val>>32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Process bitmask of exception conditions. (from vfpmodule.c)
|
||||
*/
|
||||
void vfp_raise_exceptions(ARMul_State* state, u32 exceptions, u32 inst, u32 fpscr)
|
||||
{
|
||||
int si_code = 0;
|
||||
|
||||
vfpdebug("VFP: raising exceptions %08x\n", exceptions);
|
||||
|
||||
if (exceptions == VFP_EXCEPTION_ERROR) {
|
||||
DEBUG_LOG(ARM11, "unhandled bounce %x\n", inst);
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If any of the status flags are set, update the FPSCR.
|
||||
* Comparison instructions always return at least one of
|
||||
* these flags set.
|
||||
*/
|
||||
if (exceptions & (FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V))
|
||||
fpscr &= ~(FPSCR_N|FPSCR_Z|FPSCR_C|FPSCR_V);
|
||||
|
||||
fpscr |= exceptions;
|
||||
|
||||
state->VFP[VFP_OFFSET(VFP_FPSCR)] = fpscr;
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
vfp/vfp.h - ARM VFPv3 emulation unit - vfp interface
|
||||
Copyright (C) 2003 Skyeye Develop Group
|
||||
for help please send mail to <skyeye-developer@lists.gro.clinux.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __VFP_H__
|
||||
#define __VFP_H__
|
||||
|
||||
#define DBG(...) DEBUG_LOG(ARM11, __VA_ARGS__)
|
||||
|
||||
#define vfpdebug //printf
|
||||
|
||||
#include "core/arm/interpreter/vfp/vfp_helper.h" /* for references to cdp SoftFloat functions */
|
||||
|
||||
unsigned VFPInit (ARMul_State *state);
|
||||
unsigned VFPMRC (ARMul_State * state, unsigned type, ARMword instr, ARMword * value);
|
||||
unsigned VFPMCR (ARMul_State * state, unsigned type, ARMword instr, ARMword value);
|
||||
unsigned VFPMRRC (ARMul_State * state, unsigned type, ARMword instr, ARMword * value1, ARMword * value2);
|
||||
unsigned VFPMCRR (ARMul_State * state, unsigned type, ARMword instr, ARMword value1, ARMword value2);
|
||||
unsigned VFPSTC (ARMul_State * state, unsigned type, ARMword instr, ARMword * value);
|
||||
unsigned VFPLDC (ARMul_State * state, unsigned type, ARMword instr, ARMword value);
|
||||
unsigned VFPCDP (ARMul_State * state, unsigned type, ARMword instr);
|
||||
|
||||
/* FPSID Information */
|
||||
#define VFP_FPSID_IMPLMEN 0 /* should be the same as cp15 0 c0 0*/
|
||||
#define VFP_FPSID_SW 0
|
||||
#define VFP_FPSID_SUBARCH 0x2 /* VFP version. Current is v3 (not strict) */
|
||||
#define VFP_FPSID_PARTNUM 0x1
|
||||
#define VFP_FPSID_VARIANT 0x1
|
||||
#define VFP_FPSID_REVISION 0x1
|
||||
|
||||
/* FPEXC Flags */
|
||||
#define VFP_FPEXC_EX 1<<31
|
||||
#define VFP_FPEXC_EN 1<<30
|
||||
|
||||
/* FPSCR Flags */
|
||||
#define VFP_FPSCR_NFLAG 1<<31
|
||||
#define VFP_FPSCR_ZFLAG 1<<30
|
||||
#define VFP_FPSCR_CFLAG 1<<29
|
||||
#define VFP_FPSCR_VFLAG 1<<28
|
||||
|
||||
#define VFP_FPSCR_AHP 1<<26 /* Alternative Half Precision */
|
||||
#define VFP_FPSCR_DN 1<<25 /* Default NaN */
|
||||
#define VFP_FPSCR_FZ 1<<24 /* Flush-to-zero */
|
||||
#define VFP_FPSCR_RMODE 3<<22 /* Rounding Mode */
|
||||
#define VFP_FPSCR_STRIDE 3<<20 /* Stride (vector) */
|
||||
#define VFP_FPSCR_LEN 7<<16 /* Stride (vector) */
|
||||
|
||||
#define VFP_FPSCR_IDE 1<<15 /* Input Denormal exc */
|
||||
#define VFP_FPSCR_IXE 1<<12 /* Inexact exc */
|
||||
#define VFP_FPSCR_UFE 1<<11 /* Undeflow exc */
|
||||
#define VFP_FPSCR_OFE 1<<10 /* Overflow exc */
|
||||
#define VFP_FPSCR_DZE 1<<9 /* Division by Zero exc */
|
||||
#define VFP_FPSCR_IOE 1<<8 /* Invalid Operation exc */
|
||||
|
||||
#define VFP_FPSCR_IDC 1<<7 /* Input Denormal cum exc */
|
||||
#define VFP_FPSCR_IXC 1<<4 /* Inexact cum exc */
|
||||
#define VFP_FPSCR_UFC 1<<3 /* Undeflow cum exc */
|
||||
#define VFP_FPSCR_OFC 1<<2 /* Overflow cum exc */
|
||||
#define VFP_FPSCR_DZC 1<<1 /* Division by Zero cum exc */
|
||||
#define VFP_FPSCR_IOC 1<<0 /* Invalid Operation cum exc */
|
||||
|
||||
/* Inline instructions. Note: Used in a cpp file as well */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int32_t vfp_get_float(ARMul_State * state, unsigned int reg);
|
||||
void vfp_put_float(ARMul_State * state, int32_t val, unsigned int reg);
|
||||
uint64_t vfp_get_double(ARMul_State * state, unsigned int reg);
|
||||
void vfp_put_double(ARMul_State * state, uint64_t val, unsigned int reg);
|
||||
void vfp_raise_exceptions(ARMul_State * state, uint32_t exceptions, uint32_t inst, uint32_t fpscr);
|
||||
u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
|
||||
u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr);
|
||||
|
||||
/* MRC */
|
||||
inline void VMRS(ARMul_State * state, ARMword reg, ARMword Rt, ARMword *value);
|
||||
inline void VMOVBRS(ARMul_State * state, ARMword to_arm, ARMword t, ARMword n, ARMword *value);
|
||||
inline void VMOVBRRD(ARMul_State * state, ARMword to_arm, ARMword t, ARMword t2, ARMword n, ARMword *value1, ARMword *value2);
|
||||
inline void VMOVI(ARMul_State * state, ARMword single, ARMword d, ARMword imm);
|
||||
inline void VMOVR(ARMul_State * state, ARMword single, ARMword d, ARMword imm);
|
||||
/* MCR */
|
||||
inline void VMSR(ARMul_State * state, ARMword reg, ARMword Rt);
|
||||
/* STC */
|
||||
inline int VSTM(ARMul_State * state, int type, ARMword instr, ARMword* value);
|
||||
inline int VPUSH(ARMul_State * state, int type, ARMword instr, ARMword* value);
|
||||
inline int VSTR(ARMul_State * state, int type, ARMword instr, ARMword* value);
|
||||
/* LDC */
|
||||
inline int VLDM(ARMul_State * state, int type, ARMword instr, ARMword value);
|
||||
inline int VPOP(ARMul_State * state, int type, ARMword instr, ARMword value);
|
||||
inline int VLDR(ARMul_State * state, int type, ARMword instr, ARMword value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,541 @@
|
||||
/*
|
||||
vfp/vfp.h - ARM VFPv3 emulation unit - SoftFloat lib helper
|
||||
Copyright (C) 2003 Skyeye Develop Group
|
||||
for help please send mail to <skyeye-developer@lists.gro.clinux.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following code is derivative from Linux Android kernel vfp
|
||||
* floating point support.
|
||||
*
|
||||
* Copyright (C) 2004 ARM Limited.
|
||||
* Written by Deep Blue Solutions Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef __VFP_HELPER_H__
|
||||
#define __VFP_HELPER_H__
|
||||
|
||||
/* Custom edit */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "core/arm/interpreter/armdefs.h"
|
||||
|
||||
#define u16 uint16_t
|
||||
#define u32 uint32_t
|
||||
#define u64 uint64_t
|
||||
#define s16 int16_t
|
||||
#define s32 int32_t
|
||||
#define s64 int64_t
|
||||
|
||||
#define pr_info //printf
|
||||
#define pr_debug //printf
|
||||
|
||||
static u32 fls(int x);
|
||||
#define do_div(n, base) {n/=base;}
|
||||
|
||||
/* From vfpinstr.h */
|
||||
|
||||
#define INST_CPRTDO(inst) (((inst) & 0x0f000000) == 0x0e000000)
|
||||
#define INST_CPRT(inst) ((inst) & (1 << 4))
|
||||
#define INST_CPRT_L(inst) ((inst) & (1 << 20))
|
||||
#define INST_CPRT_Rd(inst) (((inst) & (15 << 12)) >> 12)
|
||||
#define INST_CPRT_OP(inst) (((inst) >> 21) & 7)
|
||||
#define INST_CPNUM(inst) ((inst) & 0xf00)
|
||||
#define CPNUM(cp) ((cp) << 8)
|
||||
|
||||
#define FOP_MASK (0x00b00040)
|
||||
#define FOP_FMAC (0x00000000)
|
||||
#define FOP_FNMAC (0x00000040)
|
||||
#define FOP_FMSC (0x00100000)
|
||||
#define FOP_FNMSC (0x00100040)
|
||||
#define FOP_FMUL (0x00200000)
|
||||
#define FOP_FNMUL (0x00200040)
|
||||
#define FOP_FADD (0x00300000)
|
||||
#define FOP_FSUB (0x00300040)
|
||||
#define FOP_FDIV (0x00800000)
|
||||
#define FOP_EXT (0x00b00040)
|
||||
|
||||
#define FOP_TO_IDX(inst) ((inst & 0x00b00000) >> 20 | (inst & (1 << 6)) >> 4)
|
||||
|
||||
#define FEXT_MASK (0x000f0080)
|
||||
#define FEXT_FCPY (0x00000000)
|
||||
#define FEXT_FABS (0x00000080)
|
||||
#define FEXT_FNEG (0x00010000)
|
||||
#define FEXT_FSQRT (0x00010080)
|
||||
#define FEXT_FCMP (0x00040000)
|
||||
#define FEXT_FCMPE (0x00040080)
|
||||
#define FEXT_FCMPZ (0x00050000)
|
||||
#define FEXT_FCMPEZ (0x00050080)
|
||||
#define FEXT_FCVT (0x00070080)
|
||||
#define FEXT_FUITO (0x00080000)
|
||||
#define FEXT_FSITO (0x00080080)
|
||||
#define FEXT_FTOUI (0x000c0000)
|
||||
#define FEXT_FTOUIZ (0x000c0080)
|
||||
#define FEXT_FTOSI (0x000d0000)
|
||||
#define FEXT_FTOSIZ (0x000d0080)
|
||||
|
||||
#define FEXT_TO_IDX(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
|
||||
|
||||
#define vfp_get_sd(inst) ((inst & 0x0000f000) >> 11 | (inst & (1 << 22)) >> 22)
|
||||
#define vfp_get_dd(inst) ((inst & 0x0000f000) >> 12 | (inst & (1 << 22)) >> 18)
|
||||
#define vfp_get_sm(inst) ((inst & 0x0000000f) << 1 | (inst & (1 << 5)) >> 5)
|
||||
#define vfp_get_dm(inst) ((inst & 0x0000000f) | (inst & (1 << 5)) >> 1)
|
||||
#define vfp_get_sn(inst) ((inst & 0x000f0000) >> 15 | (inst & (1 << 7)) >> 7)
|
||||
#define vfp_get_dn(inst) ((inst & 0x000f0000) >> 16 | (inst & (1 << 7)) >> 3)
|
||||
|
||||
#define vfp_single(inst) (((inst) & 0x0000f00) == 0xa00)
|
||||
|
||||
#define FPSCR_N (1 << 31)
|
||||
#define FPSCR_Z (1 << 30)
|
||||
#define FPSCR_C (1 << 29)
|
||||
#define FPSCR_V (1 << 28)
|
||||
|
||||
/* -------------- */
|
||||
|
||||
/* From asm/include/vfp.h */
|
||||
|
||||
/* FPSCR bits */
|
||||
#define FPSCR_DEFAULT_NAN (1<<25)
|
||||
#define FPSCR_FLUSHTOZERO (1<<24)
|
||||
#define FPSCR_ROUND_NEAREST (0<<22)
|
||||
#define FPSCR_ROUND_PLUSINF (1<<22)
|
||||
#define FPSCR_ROUND_MINUSINF (2<<22)
|
||||
#define FPSCR_ROUND_TOZERO (3<<22)
|
||||
#define FPSCR_RMODE_BIT (22)
|
||||
#define FPSCR_RMODE_MASK (3 << FPSCR_RMODE_BIT)
|
||||
#define FPSCR_STRIDE_BIT (20)
|
||||
#define FPSCR_STRIDE_MASK (3 << FPSCR_STRIDE_BIT)
|
||||
#define FPSCR_LENGTH_BIT (16)
|
||||
#define FPSCR_LENGTH_MASK (7 << FPSCR_LENGTH_BIT)
|
||||
#define FPSCR_IOE (1<<8)
|
||||
#define FPSCR_DZE (1<<9)
|
||||
#define FPSCR_OFE (1<<10)
|
||||
#define FPSCR_UFE (1<<11)
|
||||
#define FPSCR_IXE (1<<12)
|
||||
#define FPSCR_IDE (1<<15)
|
||||
#define FPSCR_IOC (1<<0)
|
||||
#define FPSCR_DZC (1<<1)
|
||||
#define FPSCR_OFC (1<<2)
|
||||
#define FPSCR_UFC (1<<3)
|
||||
#define FPSCR_IXC (1<<4)
|
||||
#define FPSCR_IDC (1<<7)
|
||||
|
||||
/* ---------------- */
|
||||
|
||||
static inline u32 vfp_shiftright32jamming(u32 val, unsigned int shift)
|
||||
{
|
||||
if (shift) {
|
||||
if (shift < 32)
|
||||
val = val >> shift | ((val << (32 - shift)) != 0);
|
||||
else
|
||||
val = val != 0;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u64 vfp_shiftright64jamming(u64 val, unsigned int shift)
|
||||
{
|
||||
if (shift) {
|
||||
if (shift < 64)
|
||||
val = val >> shift | ((val << (64 - shift)) != 0);
|
||||
else
|
||||
val = val != 0;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 vfp_hi64to32jamming(u64 val)
|
||||
{
|
||||
u32 v;
|
||||
u32 highval = val >> 32;
|
||||
u32 lowval = val & 0xffffffff;
|
||||
|
||||
if (lowval >= 1)
|
||||
v = highval | 1;
|
||||
else
|
||||
v = highval;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline void add128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml)
|
||||
{
|
||||
*resl = nl + ml;
|
||||
*resh = nh + mh;
|
||||
if (*resl < nl)
|
||||
*resh += 1;
|
||||
}
|
||||
|
||||
static inline void sub128(u64 *resh, u64 *resl, u64 nh, u64 nl, u64 mh, u64 ml)
|
||||
{
|
||||
*resl = nl - ml;
|
||||
*resh = nh - mh;
|
||||
if (*resl > nl)
|
||||
*resh -= 1;
|
||||
}
|
||||
|
||||
static inline void mul64to128(u64 *resh, u64 *resl, u64 n, u64 m)
|
||||
{
|
||||
u32 nh, nl, mh, ml;
|
||||
u64 rh, rma, rmb, rl;
|
||||
|
||||
nl = n;
|
||||
ml = m;
|
||||
rl = (u64)nl * ml;
|
||||
|
||||
nh = n >> 32;
|
||||
rma = (u64)nh * ml;
|
||||
|
||||
mh = m >> 32;
|
||||
rmb = (u64)nl * mh;
|
||||
rma += rmb;
|
||||
|
||||
rh = (u64)nh * mh;
|
||||
rh += ((u64)(rma < rmb) << 32) + (rma >> 32);
|
||||
|
||||
rma <<= 32;
|
||||
rl += rma;
|
||||
rh += (rl < rma);
|
||||
|
||||
*resl = rl;
|
||||
*resh = rh;
|
||||
}
|
||||
|
||||
static inline void shift64left(u64 *resh, u64 *resl, u64 n)
|
||||
{
|
||||
*resh = n >> 63;
|
||||
*resl = n << 1;
|
||||
}
|
||||
|
||||
static inline u64 vfp_hi64multiply64(u64 n, u64 m)
|
||||
{
|
||||
u64 rh, rl;
|
||||
mul64to128(&rh, &rl, n, m);
|
||||
return rh | (rl != 0);
|
||||
}
|
||||
|
||||
static inline u64 vfp_estimate_div128to64(u64 nh, u64 nl, u64 m)
|
||||
{
|
||||
u64 mh, ml, remh, reml, termh, terml, z;
|
||||
|
||||
if (nh >= m)
|
||||
return ~0ULL;
|
||||
mh = m >> 32;
|
||||
if (mh << 32 <= nh) {
|
||||
z = 0xffffffff00000000ULL;
|
||||
} else {
|
||||
z = nh;
|
||||
do_div(z, mh);
|
||||
z <<= 32;
|
||||
}
|
||||
mul64to128(&termh, &terml, m, z);
|
||||
sub128(&remh, &reml, nh, nl, termh, terml);
|
||||
ml = m << 32;
|
||||
while ((s64)remh < 0) {
|
||||
z -= 0x100000000ULL;
|
||||
add128(&remh, &reml, remh, reml, mh, ml);
|
||||
}
|
||||
remh = (remh << 32) | (reml >> 32);
|
||||
if (mh << 32 <= remh) {
|
||||
z |= 0xffffffff;
|
||||
} else {
|
||||
do_div(remh, mh);
|
||||
z |= remh;
|
||||
}
|
||||
return z;
|
||||
}
|
||||
|
||||
/*
|
||||
* Operations on unpacked elements
|
||||
*/
|
||||
#define vfp_sign_negate(sign) (sign ^ 0x8000)
|
||||
|
||||
/*
|
||||
* Single-precision
|
||||
*/
|
||||
struct vfp_single {
|
||||
s16 exponent;
|
||||
u16 sign;
|
||||
u32 significand;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern s32 vfp_get_float(ARMul_State * state, unsigned int reg);
|
||||
extern void vfp_put_float(ARMul_State * state, s32 val, unsigned int reg);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* VFP_SINGLE_MANTISSA_BITS - number of bits in the mantissa
|
||||
* VFP_SINGLE_EXPONENT_BITS - number of bits in the exponent
|
||||
* VFP_SINGLE_LOW_BITS - number of low bits in the unpacked significand
|
||||
* which are not propagated to the float upon packing.
|
||||
*/
|
||||
#define VFP_SINGLE_MANTISSA_BITS (23)
|
||||
#define VFP_SINGLE_EXPONENT_BITS (8)
|
||||
#define VFP_SINGLE_LOW_BITS (32 - VFP_SINGLE_MANTISSA_BITS - 2)
|
||||
#define VFP_SINGLE_LOW_BITS_MASK ((1 << VFP_SINGLE_LOW_BITS) - 1)
|
||||
|
||||
/*
|
||||
* The bit in an unpacked float which indicates that it is a quiet NaN
|
||||
*/
|
||||
#define VFP_SINGLE_SIGNIFICAND_QNAN (1 << (VFP_SINGLE_MANTISSA_BITS - 1 + VFP_SINGLE_LOW_BITS))
|
||||
|
||||
/*
|
||||
* Operations on packed single-precision numbers
|
||||
*/
|
||||
#define vfp_single_packed_sign(v) ((v) & 0x80000000)
|
||||
#define vfp_single_packed_negate(v) ((v) ^ 0x80000000)
|
||||
#define vfp_single_packed_abs(v) ((v) & ~0x80000000)
|
||||
#define vfp_single_packed_exponent(v) (((v) >> VFP_SINGLE_MANTISSA_BITS) & ((1 << VFP_SINGLE_EXPONENT_BITS) - 1))
|
||||
#define vfp_single_packed_mantissa(v) ((v) & ((1 << VFP_SINGLE_MANTISSA_BITS) - 1))
|
||||
|
||||
/*
|
||||
* Unpack a single-precision float. Note that this returns the magnitude
|
||||
* of the single-precision float mantissa with the 1. if necessary,
|
||||
* aligned to bit 30.
|
||||
*/
|
||||
static inline void vfp_single_unpack(struct vfp_single *s, s32 val)
|
||||
{
|
||||
u32 significand;
|
||||
|
||||
s->sign = vfp_single_packed_sign(val) >> 16,
|
||||
s->exponent = vfp_single_packed_exponent(val);
|
||||
|
||||
significand = (u32) val;
|
||||
significand = (significand << (32 - VFP_SINGLE_MANTISSA_BITS)) >> 2;
|
||||
if (s->exponent && s->exponent != 255)
|
||||
significand |= 0x40000000;
|
||||
s->significand = significand;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-pack a single-precision float. This assumes that the float is
|
||||
* already normalised such that the MSB is bit 30, _not_ bit 31.
|
||||
*/
|
||||
static inline s32 vfp_single_pack(struct vfp_single *s)
|
||||
{
|
||||
u32 val;
|
||||
val = (s->sign << 16) +
|
||||
(s->exponent << VFP_SINGLE_MANTISSA_BITS) +
|
||||
(s->significand >> VFP_SINGLE_LOW_BITS);
|
||||
return (s32)val;
|
||||
}
|
||||
|
||||
#define VFP_NUMBER (1<<0)
|
||||
#define VFP_ZERO (1<<1)
|
||||
#define VFP_DENORMAL (1<<2)
|
||||
#define VFP_INFINITY (1<<3)
|
||||
#define VFP_NAN (1<<4)
|
||||
#define VFP_NAN_SIGNAL (1<<5)
|
||||
|
||||
#define VFP_QNAN (VFP_NAN)
|
||||
#define VFP_SNAN (VFP_NAN|VFP_NAN_SIGNAL)
|
||||
|
||||
static inline int vfp_single_type(struct vfp_single *s)
|
||||
{
|
||||
int type = VFP_NUMBER;
|
||||
if (s->exponent == 255) {
|
||||
if (s->significand == 0)
|
||||
type = VFP_INFINITY;
|
||||
else if (s->significand & VFP_SINGLE_SIGNIFICAND_QNAN)
|
||||
type = VFP_QNAN;
|
||||
else
|
||||
type = VFP_SNAN;
|
||||
} else if (s->exponent == 0) {
|
||||
if (s->significand == 0)
|
||||
type |= VFP_ZERO;
|
||||
else
|
||||
type |= VFP_DENORMAL;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single *vs, u32 fpscr, u32 exceptions, const char *func);
|
||||
|
||||
/*
|
||||
* Double-precision
|
||||
*/
|
||||
struct vfp_double {
|
||||
s16 exponent;
|
||||
u16 sign;
|
||||
u64 significand;
|
||||
};
|
||||
|
||||
/*
|
||||
* VFP_REG_ZERO is a special register number for vfp_get_double
|
||||
* which returns (double)0.0. This is useful for the compare with
|
||||
* zero instructions.
|
||||
*/
|
||||
#ifdef CONFIG_VFPv3
|
||||
#define VFP_REG_ZERO 32
|
||||
#else
|
||||
#define VFP_REG_ZERO 16
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern u64 vfp_get_double(ARMul_State * state, unsigned int reg);
|
||||
extern void vfp_put_double(ARMul_State * state, u64 val, unsigned int reg);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#define VFP_DOUBLE_MANTISSA_BITS (52)
|
||||
#define VFP_DOUBLE_EXPONENT_BITS (11)
|
||||
#define VFP_DOUBLE_LOW_BITS (64 - VFP_DOUBLE_MANTISSA_BITS - 2)
|
||||
#define VFP_DOUBLE_LOW_BITS_MASK ((1 << VFP_DOUBLE_LOW_BITS) - 1)
|
||||
|
||||
/*
|
||||
* The bit in an unpacked double which indicates that it is a quiet NaN
|
||||
*/
|
||||
#define VFP_DOUBLE_SIGNIFICAND_QNAN (1ULL << (VFP_DOUBLE_MANTISSA_BITS - 1 + VFP_DOUBLE_LOW_BITS))
|
||||
|
||||
/*
|
||||
* Operations on packed single-precision numbers
|
||||
*/
|
||||
#define vfp_double_packed_sign(v) ((v) & (1ULL << 63))
|
||||
#define vfp_double_packed_negate(v) ((v) ^ (1ULL << 63))
|
||||
#define vfp_double_packed_abs(v) ((v) & ~(1ULL << 63))
|
||||
#define vfp_double_packed_exponent(v) (((v) >> VFP_DOUBLE_MANTISSA_BITS) & ((1 << VFP_DOUBLE_EXPONENT_BITS) - 1))
|
||||
#define vfp_double_packed_mantissa(v) ((v) & ((1ULL << VFP_DOUBLE_MANTISSA_BITS) - 1))
|
||||
|
||||
/*
|
||||
* Unpack a double-precision float. Note that this returns the magnitude
|
||||
* of the double-precision float mantissa with the 1. if necessary,
|
||||
* aligned to bit 62.
|
||||
*/
|
||||
static inline void vfp_double_unpack(struct vfp_double *s, s64 val)
|
||||
{
|
||||
u64 significand;
|
||||
|
||||
s->sign = vfp_double_packed_sign(val) >> 48;
|
||||
s->exponent = vfp_double_packed_exponent(val);
|
||||
|
||||
significand = (u64) val;
|
||||
significand = (significand << (64 - VFP_DOUBLE_MANTISSA_BITS)) >> 2;
|
||||
if (s->exponent && s->exponent != 2047)
|
||||
significand |= (1ULL << 62);
|
||||
s->significand = significand;
|
||||
}
|
||||
|
||||
/*
|
||||
* Re-pack a double-precision float. This assumes that the float is
|
||||
* already normalised such that the MSB is bit 30, _not_ bit 31.
|
||||
*/
|
||||
static inline s64 vfp_double_pack(struct vfp_double *s)
|
||||
{
|
||||
u64 val;
|
||||
val = ((u64)s->sign << 48) +
|
||||
((u64)s->exponent << VFP_DOUBLE_MANTISSA_BITS) +
|
||||
(s->significand >> VFP_DOUBLE_LOW_BITS);
|
||||
return (s64)val;
|
||||
}
|
||||
|
||||
static inline int vfp_double_type(struct vfp_double *s)
|
||||
{
|
||||
int type = VFP_NUMBER;
|
||||
if (s->exponent == 2047) {
|
||||
if (s->significand == 0)
|
||||
type = VFP_INFINITY;
|
||||
else if (s->significand & VFP_DOUBLE_SIGNIFICAND_QNAN)
|
||||
type = VFP_QNAN;
|
||||
else
|
||||
type = VFP_SNAN;
|
||||
} else if (s->exponent == 0) {
|
||||
if (s->significand == 0)
|
||||
type |= VFP_ZERO;
|
||||
else
|
||||
type |= VFP_DENORMAL;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double *vd, u32 fpscr, u32 exceptions, const char *func);
|
||||
|
||||
u32 vfp_estimate_sqrt_significand(u32 exponent, u32 significand);
|
||||
|
||||
/*
|
||||
* A special flag to tell the normalisation code not to normalise.
|
||||
*/
|
||||
#define VFP_NAN_FLAG 0x100
|
||||
|
||||
/*
|
||||
* A bit pattern used to indicate the initial (unset) value of the
|
||||
* exception mask, in case nothing handles an instruction. This
|
||||
* doesn't include the NAN flag, which get masked out before
|
||||
* we check for an error.
|
||||
*/
|
||||
#define VFP_EXCEPTION_ERROR ((u32)-1 & ~VFP_NAN_FLAG)
|
||||
|
||||
/*
|
||||
* A flag to tell vfp instruction type.
|
||||
* OP_SCALAR - this operation always operates in scalar mode
|
||||
* OP_SD - the instruction exceptionally writes to a single precision result.
|
||||
* OP_DD - the instruction exceptionally writes to a double precision result.
|
||||
* OP_SM - the instruction exceptionally reads from a single precision operand.
|
||||
*/
|
||||
#define OP_SCALAR (1 << 0)
|
||||
#define OP_SD (1 << 1)
|
||||
#define OP_DD (1 << 1)
|
||||
#define OP_SM (1 << 2)
|
||||
|
||||
struct op {
|
||||
u32 (* const fn)(ARMul_State* state, int dd, int dn, int dm, u32 fpscr);
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
static inline u32 fls(int x)
|
||||
{
|
||||
int r = 32;
|
||||
|
||||
if (!x)
|
||||
return 0;
|
||||
if (!(x & 0xffff0000u)) {
|
||||
x <<= 16;
|
||||
r -= 16;
|
||||
}
|
||||
if (!(x & 0xff000000u)) {
|
||||
x <<= 8;
|
||||
r -= 8;
|
||||
}
|
||||
if (!(x & 0xf0000000u)) {
|
||||
x <<= 4;
|
||||
r -= 4;
|
||||
}
|
||||
if (!(x & 0xc0000000u)) {
|
||||
x <<= 2;
|
||||
r -= 2;
|
||||
}
|
||||
if (!(x & 0x80000000u)) {
|
||||
x <<= 1;
|
||||
r -= 1;
|
||||
}
|
||||
return r;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue