Make busy wait special casing safer.

This commit is contained in:
Tyler Stachecki 2014-07-29 09:47:01 -04:00
parent 9c4fecc2e0
commit 9bd494b4e0
6 changed files with 49 additions and 44 deletions

View file

@ -37,9 +37,9 @@ int VR4300_ERET(struct vr4300 *vr4300, uint64_t unused(rs), uint64_t rt) {
exdc_latch->common.fault = ~0;
icrf_latch->common.fault = ~0;
vr4300->regs[PIPELINE_CYCLE_TYPE] = 0;
pipeline->exception_history = 0;
pipeline->fault_present = true;
pipeline->skip_stages = 0;
vr4300->regs[VR4300_CP0_REGISTER_STATUS] = status;
// vr4300->llbit = 0;

View file

@ -66,6 +66,14 @@ enum vr4300_register {
// Miscellanious registers.
VR4300_REGISTER_HI, VR4300_REGISTER_LO,
VR4300_CP1_FCR0, VR4300_CP1_FCR31,
// Pipeline cycle type flag.
//
// Putting these here along with the other registers allows us to
// correctly (and cheaply) detect that a busy-wait loop instruction
// is retiring.
PIPELINE_CYCLE_TYPE,
NUM_VR4300_REGISTERS
};

View file

@ -27,27 +27,30 @@ const char *vr4300_fault_mnemonics[NUM_VR4300_FAULTS] = {
};
// Sets attributes common to all exceptions.
static void vr4300_common_exceptions(struct vr4300_pipeline *pipeline) {
static void vr4300_common_exceptions(struct vr4300 *vr4300) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
pipeline->icrf_latch.segment = get_default_segment();
pipeline->exdc_latch.segment = get_default_segment();
vr4300->regs[PIPELINE_CYCLE_TYPE] = 0;
pipeline->exception_history = 0;
pipeline->fault_present = true;
pipeline->cycles_to_stall = 2;
pipeline->skip_stages = 0;
}
// Sets attributes common to all interlocks.
static void vr4300_common_interlocks(struct vr4300_pipeline *pipeline,
static void vr4300_common_interlocks(struct vr4300 *vr4300,
unsigned cycles_to_stall, unsigned skip_stages) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
pipeline->cycles_to_stall = cycles_to_stall;
pipeline->skip_stages = skip_stages;
vr4300->regs[PIPELINE_CYCLE_TYPE] = skip_stages;
}
// Raise a fault that originated in the DC stage.
static void vr4300_dc_fault(struct vr4300_pipeline *pipeline,
enum vr4300_fault_id fault) {
vr4300_common_exceptions(pipeline);
static void vr4300_dc_fault(struct vr4300 *vr4300, enum vr4300_fault_id fault) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
vr4300_common_exceptions(vr4300);
pipeline->dcwb_latch.common.fault = fault;
pipeline->exdc_latch.common.fault = fault;
pipeline->rfex_latch.common.fault = fault;
@ -55,9 +58,10 @@ static void vr4300_dc_fault(struct vr4300_pipeline *pipeline,
}
// Raise a fault that originated in the EX stage.
static void vr4300_ex_fault(struct vr4300_pipeline *pipeline,
enum vr4300_fault_id fault) {
vr4300_common_exceptions(pipeline);
static void vr4300_ex_fault(struct vr4300 *vr4300, enum vr4300_fault_id fault) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
vr4300_common_exceptions(vr4300);
pipeline->exdc_latch.common.fault = fault;
pipeline->rfex_latch.common.fault = fault;
pipeline->icrf_latch.common.fault = fault;
@ -96,7 +100,7 @@ void VR4300_CPU(unused(struct vr4300 *vr4300)) {
vr4300->regs[VR4300_CP0_REGISTER_CAUSE] = (cause & ~0xFF) | (1 << 28) | 0x2C;
vr4300->regs[VR4300_CP0_REGISTER_EPC] = epc;
vr4300_ex_fault(pipeline, VR4300_FAULT_CPU);
vr4300_ex_fault(vr4300, VR4300_FAULT_CPU);
vr4300->pipeline.icrf_latch.pc = (status & 0x400000)
? 0xFFFFFFFFBFC00280ULL
: 0xFFFFFFFF80000080ULL;
@ -109,12 +113,11 @@ void VR4300_DADE(unused(struct vr4300 *vr4300)) {
// DCB: Data cache busy interlock.
void VR4300_DCB(struct vr4300 *vr4300) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
struct vr4300_exdc_latch *exdc_latch = &pipeline->exdc_latch;
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
struct vr4300_bus_request *request = &exdc_latch->request;
uint32_t word;
vr4300_common_interlocks(pipeline, MEMORY_DATA_CYCLE_DELAY, 5);
vr4300_common_interlocks(vr4300, MEMORY_DATA_CYCLE_DELAY, 5);
bus_read_word(vr4300->bus, request->address & ~0x3ULL, &word);
if (!request->two_words) {
@ -142,9 +145,8 @@ void VR4300_IADE(unused(struct vr4300 *vr4300)) {
// ICB: Instruction cache busy interlock.
void VR4300_ICB(unused(struct vr4300 *vr4300)) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
struct vr4300_icrf_latch *icrf_latch = &pipeline->icrf_latch;
struct vr4300_rfex_latch *rfex_latch = &pipeline->rfex_latch;
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
const struct segment *segment = icrf_latch->segment;
uint32_t line[8];
@ -152,7 +154,7 @@ void VR4300_ICB(unused(struct vr4300 *vr4300)) {
unsigned i;
/* Raise interlock condition, get virtual address. */
vr4300_common_interlocks(pipeline, ICACHE_ACCESS_DELAY, 4);
vr4300_common_interlocks(vr4300, ICACHE_ACCESS_DELAY, 4);
paddr = (icrf_latch->common.pc - segment->offset) & ~0x1C;
/* Fill the cache line. */
@ -202,7 +204,7 @@ void VR4300_INTR(unused(struct vr4300 *vr4300)) {
vr4300->regs[VR4300_CP0_REGISTER_CAUSE] = cause & ~0xFF;
vr4300->regs[VR4300_CP0_REGISTER_EPC] = epc;
vr4300_dc_fault(pipeline, VR4300_FAULT_INTR);
vr4300_dc_fault(vr4300, VR4300_FAULT_INTR);
vr4300->pipeline.icrf_latch.pc = (status & 0x400000)
? 0xFFFFFFFFBFC00280ULL
: 0xFFFFFFFF80000080ULL;
@ -215,16 +217,13 @@ void VR4300_LDI(struct vr4300 *vr4300) {
// We'll do EX again, but clear the 'busy' flag.
exdc_latch->request.type = VR4300_BUS_REQUEST_NONE;
vr4300_common_interlocks(pipeline, 0, 2);
vr4300_common_interlocks(vr4300, 0, 2);
}
// RST: External reset exception.
void VR4300_RST(struct vr4300 *vr4300) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
// Prepare pipeline for restart.
vr4300->pipeline.icrf_latch.pc = 0xFFFFFFFFBFC00000ULL;
vr4300_dc_fault(pipeline, VR4300_FAULT_RST);
vr4300_dc_fault(vr4300, VR4300_FAULT_RST);
// Cold reset exception.
if (vr4300->signals & VR4300_SIGNAL_COLDRESET) {
@ -246,13 +245,12 @@ void VR4300_RST(struct vr4300 *vr4300) {
// UNC: Uncached read interlock.
void VR4300_UNC(struct vr4300 *vr4300) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
struct vr4300_icrf_latch *icrf_latch = &pipeline->icrf_latch;
struct vr4300_rfex_latch *rfex_latch = &pipeline->rfex_latch;
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
const struct segment *segment = icrf_latch->segment;
uint64_t address;
vr4300_common_interlocks(pipeline, MEMORY_CODE_CYCLE_DELAY, 4);
vr4300_common_interlocks(vr4300, MEMORY_CODE_CYCLE_DELAY, 4);
address = icrf_latch->common.pc - segment->offset;
bus_read_word(vr4300->bus, address, &rfex_latch->iw);

View file

@ -215,6 +215,7 @@ int VR4300_ANDI_ORI_XORI(struct vr4300 *vr4300, uint64_t rs, uint64_t rt) {
int VR4300_BEQ(struct vr4300 *vr4300, uint64_t rs, uint64_t rt) {
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
uint32_t iw = rfex_latch->iw;
int64_t offset = (uint64_t) ((int16_t) iw) << 2;
@ -224,9 +225,9 @@ int VR4300_BEQ(struct vr4300 *vr4300, uint64_t rs, uint64_t rt) {
icrf_latch->pc = rfex_latch->common.pc + (offset + 4);
if (icrf_latch->pc == rfex_latch->pc && GET_RS(iw) == 0 && GET_RT(iw)) {
fprintf(stderr, "Detected busy wait loop.\n");
abort();
if (icrf_latch->pc == rfex_latch->common.pc && GET_RS(iw) == 0 && GET_RT(iw)) {
exdc_latch->dest = PIPELINE_CYCLE_TYPE;
exdc_latch->result = 0;
}
return 0;
@ -795,13 +796,14 @@ int VR4300_INV(struct vr4300 *vr4300,
int VR4300_J(struct vr4300 *vr4300, uint64_t rs, uint64_t rt) {
struct vr4300_icrf_latch *icrf_latch = &vr4300->pipeline.icrf_latch;
struct vr4300_rfex_latch *rfex_latch = &vr4300->pipeline.rfex_latch;
struct vr4300_exdc_latch *exdc_latch = &vr4300->pipeline.exdc_latch;
uint32_t target = (rfex_latch->iw << 2) & 0x0FFFFFFF;
icrf_latch->pc = (rfex_latch->common.pc & ~0x0FFFFFFFULL) | target;
if (icrf_latch->pc == rfex_latch->common.pc) {
fprintf(stderr, "Detected busy wait loop.\n");
abort();
exdc_latch->dest = PIPELINE_CYCLE_TYPE;
exdc_latch->result = 0;
}
return 0;

View file

@ -358,7 +358,7 @@ static void vr4300_cycle_slow_dc(struct vr4300 *vr4300) {
if (vr4300_ic_stage(vr4300))
return;
pipeline->skip_stages = 0;
vr4300->regs[PIPELINE_CYCLE_TYPE] = 0;
}
// Advances the processor pipeline by one pclock.
@ -390,7 +390,7 @@ static void vr4300_cycle_slow_ex(struct vr4300 *vr4300) {
if (vr4300_ic_stage(vr4300))
return;
pipeline->skip_stages = 0;
vr4300->regs[PIPELINE_CYCLE_TYPE] = 0;
}
// Advances the processor pipeline by one pclock.
@ -433,7 +433,7 @@ static void vr4300_cycle_slow_ex_fixdc(struct vr4300 *vr4300) {
if (vr4300_ic_stage(vr4300))
return;
pipeline->skip_stages = 0;
vr4300->regs[PIPELINE_CYCLE_TYPE] = 0;
}
// Advances the processor pipeline by one pclock.
@ -456,7 +456,7 @@ static void vr4300_cycle_slow_rf(struct vr4300 *vr4300) {
if (vr4300_ic_stage(vr4300))
return;
pipeline->skip_stages = 0;
vr4300->regs[PIPELINE_CYCLE_TYPE] = 0;
}
// Advances the processor pipeline by one pclock.
@ -464,12 +464,10 @@ static void vr4300_cycle_slow_rf(struct vr4300 *vr4300) {
//
// Starts from IC stage (RF resolved an interlock).
static void vr4300_cycle_slow_ic(struct vr4300 *vr4300) {
struct vr4300_pipeline *pipeline = &vr4300->pipeline;
if (vr4300_ic_stage(vr4300))
return;
pipeline->skip_stages = 0;
vr4300->regs[PIPELINE_CYCLE_TYPE] = 0;
}
// LUT of stages for fault handling.
@ -503,8 +501,8 @@ void vr4300_cycle(struct vr4300 *vr4300) {
// Ordinarily, we would need to check every pipeline stage to see if it is
// aborted, and conditionally not execute it. Since faults are rare, we'll
// only bother checking for aborted stages when we know they can be present.
if (pipeline->fault_present + pipeline->skip_stages) {
pipeline_function_lut[pipeline->skip_stages](vr4300);
if (pipeline->fault_present + vr4300->regs[PIPELINE_CYCLE_TYPE]) {
pipeline_function_lut[vr4300->regs[PIPELINE_CYCLE_TYPE]](vr4300);
return;
}

View file

@ -75,7 +75,6 @@ struct vr4300_pipeline {
unsigned exception_history;
unsigned cycles_to_stall;
unsigned skip_stages;
bool fault_present;
};