mips: Add MT halting and waking of VPEs
+ some partial support for TC's. Signed-off-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
This commit is contained in:
parent
9e56e75624
commit
f249412c74
@ -618,6 +618,14 @@ enum {
|
|||||||
/* Dummy exception for conditional stores. */
|
/* Dummy exception for conditional stores. */
|
||||||
#define EXCP_SC 0x100
|
#define EXCP_SC 0x100
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is an interrnally generated WAKE request line.
|
||||||
|
* It is driven by the CPU itself. Raised when the MT
|
||||||
|
* block wants to wake a VPE from an inactive state and
|
||||||
|
* cleared when VPE goes from active to inactive.
|
||||||
|
*/
|
||||||
|
#define CPU_INTERRUPT_WAKE CPU_INTERRUPT_TGT_INT_0
|
||||||
|
|
||||||
int cpu_mips_exec(CPUMIPSState *s);
|
int cpu_mips_exec(CPUMIPSState *s);
|
||||||
CPUMIPSState *cpu_mips_init(const char *cpu_model);
|
CPUMIPSState *cpu_mips_init(const char *cpu_model);
|
||||||
//~ uint32_t cpu_mips_get_clock (void);
|
//~ uint32_t cpu_mips_get_clock (void);
|
||||||
@ -658,6 +666,37 @@ static inline void cpu_set_tls(CPUState *env, target_ulong newtls)
|
|||||||
env->tls_value = newtls;
|
env->tls_value = newtls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int mips_vpe_active(CPUState *env)
|
||||||
|
{
|
||||||
|
int active = 1;
|
||||||
|
|
||||||
|
/* Check that the VPE is enabled. */
|
||||||
|
if (!(env->mvp->CP0_MVPControl & (1 << CP0MVPCo_EVP))) {
|
||||||
|
active = 0;
|
||||||
|
}
|
||||||
|
/* Check that the VPE is actived. */
|
||||||
|
if (!(env->CP0_VPEConf0 & (1 << CP0VPEC0_VPA))) {
|
||||||
|
active = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now verify that there are active thread contexts in the VPE.
|
||||||
|
|
||||||
|
This assumes the CPU model will internally reschedule threads
|
||||||
|
if the active one goes to sleep. If there are no threads available
|
||||||
|
the active one will be in a sleeping state, and we can turn off
|
||||||
|
the entire VPE. */
|
||||||
|
if (!(env->active_tc.CP0_TCStatus & (1 << CP0TCSt_A))) {
|
||||||
|
/* TC is not activated. */
|
||||||
|
active = 0;
|
||||||
|
}
|
||||||
|
if (env->active_tc.CP0_TCHalt & 1) {
|
||||||
|
/* TC is in halt state. */
|
||||||
|
active = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int cpu_has_work(CPUState *env)
|
static inline int cpu_has_work(CPUState *env)
|
||||||
{
|
{
|
||||||
int has_work = 0;
|
int has_work = 0;
|
||||||
@ -670,6 +709,18 @@ static inline int cpu_has_work(CPUState *env)
|
|||||||
has_work = 1;
|
has_work = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* MIPS-MT has the ability to halt the CPU. */
|
||||||
|
if (env->CP0_Config3 & (1 << CP0C3_MT)) {
|
||||||
|
/* The QEMU model will issue an _WAKE request whenever the CPUs
|
||||||
|
should be woken up. */
|
||||||
|
if (env->interrupt_request & CPU_INTERRUPT_WAKE) {
|
||||||
|
has_work = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mips_vpe_active(env)) {
|
||||||
|
has_work = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
return has_work;
|
return has_work;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -749,6 +749,46 @@ void helper_sdm (target_ulong addr, target_ulong reglist, uint32_t mem_idx)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
|
/* SMP helpers. */
|
||||||
|
static int mips_vpe_is_wfi(CPUState *c)
|
||||||
|
{
|
||||||
|
/* If the VPE is halted but otherwise active, it means it's waiting for
|
||||||
|
an interrupt. */
|
||||||
|
return c->halted && mips_vpe_active(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mips_vpe_wake(CPUState *c)
|
||||||
|
{
|
||||||
|
/* Dont set ->halted = 0 directly, let it be done via cpu_has_work
|
||||||
|
because there might be other conditions that state that c should
|
||||||
|
be sleeping. */
|
||||||
|
cpu_interrupt(c, CPU_INTERRUPT_WAKE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mips_vpe_sleep(CPUState *c)
|
||||||
|
{
|
||||||
|
/* The VPE was shut off, really go to bed.
|
||||||
|
Reset any old _WAKE requests. */
|
||||||
|
c->halted = 1;
|
||||||
|
cpu_reset_interrupt(c, CPU_INTERRUPT_WAKE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mips_tc_wake(CPUState *c, int tc)
|
||||||
|
{
|
||||||
|
/* FIXME: TC reschedule. */
|
||||||
|
if (mips_vpe_active(c) && !mips_vpe_is_wfi(c)) {
|
||||||
|
mips_vpe_wake(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void mips_tc_sleep(CPUState *c, int tc)
|
||||||
|
{
|
||||||
|
/* FIXME: TC reschedule. */
|
||||||
|
if (!mips_vpe_active(c)) {
|
||||||
|
mips_vpe_sleep(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* tc should point to an int with the value of the global TC index.
|
/* tc should point to an int with the value of the global TC index.
|
||||||
This function will transform it into a local index within the
|
This function will transform it into a local index within the
|
||||||
returned CPUState.
|
returned CPUState.
|
||||||
@ -1340,6 +1380,11 @@ void helper_mtc0_tchalt (target_ulong arg1)
|
|||||||
env->active_tc.CP0_TCHalt = arg1 & 0x1;
|
env->active_tc.CP0_TCHalt = arg1 & 0x1;
|
||||||
|
|
||||||
// TODO: Halt TC / Restart (if allocated+active) TC.
|
// TODO: Halt TC / Restart (if allocated+active) TC.
|
||||||
|
if (env->active_tc.CP0_TCHalt & 1) {
|
||||||
|
mips_tc_sleep(env, env->current_tc);
|
||||||
|
} else {
|
||||||
|
mips_tc_wake(env, env->current_tc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void helper_mttc0_tchalt (target_ulong arg1)
|
void helper_mttc0_tchalt (target_ulong arg1)
|
||||||
@ -1353,6 +1398,12 @@ void helper_mttc0_tchalt (target_ulong arg1)
|
|||||||
other->active_tc.CP0_TCHalt = arg1;
|
other->active_tc.CP0_TCHalt = arg1;
|
||||||
else
|
else
|
||||||
other->tcs[other_tc].CP0_TCHalt = arg1;
|
other->tcs[other_tc].CP0_TCHalt = arg1;
|
||||||
|
|
||||||
|
if (arg1 & 1) {
|
||||||
|
mips_tc_sleep(other, other_tc);
|
||||||
|
} else {
|
||||||
|
mips_tc_wake(other, other_tc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void helper_mtc0_tccontext (target_ulong arg1)
|
void helper_mtc0_tccontext (target_ulong arg1)
|
||||||
@ -1858,14 +1909,36 @@ target_ulong helper_emt(void)
|
|||||||
|
|
||||||
target_ulong helper_dvpe(void)
|
target_ulong helper_dvpe(void)
|
||||||
{
|
{
|
||||||
// TODO
|
CPUState *other_cpu = first_cpu;
|
||||||
return 0;
|
target_ulong prev = env->mvp->CP0_MVPControl;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* Turn off all VPEs except the one executing the dvpe. */
|
||||||
|
if (other_cpu != env) {
|
||||||
|
other_cpu->mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP);
|
||||||
|
mips_vpe_sleep(other_cpu);
|
||||||
|
}
|
||||||
|
other_cpu = other_cpu->next_cpu;
|
||||||
|
} while (other_cpu);
|
||||||
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
target_ulong helper_evpe(void)
|
target_ulong helper_evpe(void)
|
||||||
{
|
{
|
||||||
// TODO
|
CPUState *other_cpu = first_cpu;
|
||||||
return 0;
|
target_ulong prev = env->mvp->CP0_MVPControl;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (other_cpu != env
|
||||||
|
/* If the VPE is WFI, dont distrub it's sleep. */
|
||||||
|
&& !mips_vpe_is_wfi(other_cpu)) {
|
||||||
|
/* Enable the VPE. */
|
||||||
|
other_cpu->mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP);
|
||||||
|
mips_vpe_wake(other_cpu); /* And wake it up. */
|
||||||
|
}
|
||||||
|
other_cpu = other_cpu->next_cpu;
|
||||||
|
} while (other_cpu);
|
||||||
|
return prev;
|
||||||
}
|
}
|
||||||
#endif /* !CONFIG_USER_ONLY */
|
#endif /* !CONFIG_USER_ONLY */
|
||||||
|
|
||||||
@ -2213,6 +2286,7 @@ void helper_pmon (int function)
|
|||||||
void helper_wait (void)
|
void helper_wait (void)
|
||||||
{
|
{
|
||||||
env->halted = 1;
|
env->halted = 1;
|
||||||
|
cpu_reset_interrupt(env, CPU_INTERRUPT_WAKE);
|
||||||
helper_raise_exception(EXCP_HLT);
|
helper_raise_exception(EXCP_HLT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user