Line 49... |
Line 49... |
#include <assert.h>
|
#include <assert.h>
|
|
|
/** CPU identification code (contents of register CP0[15], PRId */
|
/** CPU identification code (contents of register CP0[15], PRId */
|
#define R3000_ID (0x00000200)
|
#define R3000_ID (0x00000200)
|
|
|
|
/** Number of hardware interrupt inputs (irq0 is NMI) */
|
|
#define NUM_HW_IRQS (8)
|
|
|
/** Set to !=0 to disable file logging (much faster simulation) */
|
/** Set to !=0 to disable file logging (much faster simulation) */
|
/* alternately you can just set an unreachable log trigger address */
|
/* alternately you can just set an unreachable log trigger address */
|
#define FILE_LOGGING_DISABLED (0)
|
#define FILE_LOGGING_DISABLED (0)
|
/** Define to enable cache simulation (unimplemented) */
|
/** Define to enable cache simulation (unimplemented) */
|
//#define ENABLE_CACHE
|
//#define ENABLE_CACHE
|
Line 300... |
Line 303... |
|
|
|
|
/** Length of debugging jump target queue */
|
/** Length of debugging jump target queue */
|
#define TRACE_BUFFER_SIZE (32)
|
#define TRACE_BUFFER_SIZE (32)
|
|
|
|
/** Assorted debug & trace info */
|
typedef struct s_trace {
|
typedef struct s_trace {
|
unsigned int buf[TRACE_BUFFER_SIZE]; /**< queue of last jump targets */
|
unsigned int buf[TRACE_BUFFER_SIZE]; /**< queue of last jump targets */
|
unsigned int next; /**< internal queue head pointer */
|
unsigned int next; /**< internal queue head pointer */
|
FILE *log; /**< text log file or NULL */
|
FILE *log; /**< text log file or NULL */
|
int log_triggered; /**< !=0 if log has been triggered */
|
int log_triggered; /**< !=0 if log has been triggered */
|
uint32_t log_trigger_address; /**< */
|
uint32_t log_trigger_address; /**< */
|
int pr[32]; /**< last value of register bank */
|
int pr[32]; /**< last value of register bank */
|
int hi, lo, epc, status; /**< last value of internal regs */
|
int hi, lo, epc, status; /**< last value of internal regs */
|
int disasm_ptr; /**< disassembly pointer */
|
int disasm_ptr; /**< disassembly pointer */
|
|
/** Cycles remaining to trigger irq[i], or -1 if irq inactive */
|
|
int32_t irq_trigger_countdown[NUM_HW_IRQS]; /**< (in instructions) */
|
} t_trace;
|
} t_trace;
|
|
|
typedef struct s_state {
|
typedef struct s_state {
|
unsigned failed_assertions; /**< assertion bitmap */
|
unsigned failed_assertions; /**< assertion bitmap */
|
unsigned faulty_address; /**< addr that failed assertion */
|
unsigned faulty_address; /**< addr that failed assertion */
|
uint32_t do_unaligned; /**< !=0 to enable unaligned L/S */
|
uint32_t do_unaligned; /**< !=0 to enable unaligned L/S */
|
uint32_t breakpoint; /**< BP address of 0xffffffff */
|
uint32_t breakpoint; /**< BP address of 0xffffffff */
|
|
|
int delay_slot; /**< !=0 if prev. instruction was a branch */
|
int delay_slot; /**< !=0 if prev. instruction was a branch */
|
|
uint32_t instruction_ctr; /**< # of instructions executed since reset */
|
|
|
int r[32];
|
int r[32];
|
int opcode;
|
int opcode;
|
int pc, pc_next, epc;
|
int pc, pc_next, epc;
|
uint32_t op_addr; /**< address of opcode being simulated */
|
uint32_t op_addr; /**< address of opcode being simulated */
|
Line 436... |
Line 443... |
void reverse_endianess(uint8_t *data, uint32_t bytes);
|
void reverse_endianess(uint8_t *data, uint32_t bytes);
|
|
|
/* Hardware simulation */
|
/* Hardware simulation */
|
int mem_read(t_state *s, int size, unsigned int address, int log);
|
int mem_read(t_state *s, int size, unsigned int address, int log);
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log);
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log);
|
|
void debug_reg_write(t_state *s, uint32_t address, uint32_t data);
|
void start_load(t_state *s, uint32_t addr, int rt, int data);
|
void start_load(t_state *s, uint32_t addr, int rt, int data);
|
|
uint32_t simulate_hw_irqs(t_state *s);
|
|
|
|
|
/*---- Local functions -------------------------------------------------------*/
|
/*---- Local functions -------------------------------------------------------*/
|
|
|
/*---- Call & ret tracing (EARLY DRAFT) --------------------------------------*/
|
/*---- Call & ret tracing (EARLY DRAFT) --------------------------------------*/
|
Line 614... |
Line 623... |
|
|
//log_read(s, full_address, value, size, log);
|
//log_read(s, full_address, value, size, log);
|
return(value);
|
return(value);
|
}
|
}
|
|
|
/** Write memory */
|
/** Write to debug register */
|
|
void debug_reg_write(t_state *s, uint32_t address, uint32_t data){
|
|
|
|
printf("DEBUG REG[%04x]=%08x\n", address & 0xffff, data);
|
|
}
|
|
|
|
/** Write to memory, including simulated i/o */
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log){
|
void mem_write(t_state *s, int size, unsigned address, unsigned value, int log){
|
unsigned int i, ptr, mask, dvalue, b0, b1, b2, b3;
|
unsigned int i, ptr, mask, dvalue, b0, b1, b2, b3;
|
|
|
if(log_enabled(s)){
|
if(log_enabled(s)){
|
b0 = value & 0x000000ff;
|
b0 = value & 0x000000ff;
|
Line 666... |
Line 681... |
s->op_addr, address, mask, dvalue);
|
s->op_addr, address, mask, dvalue);
|
}
|
}
|
|
|
/* Print anything that's written to a debug register, otherwise ignore it */
|
/* Print anything that's written to a debug register, otherwise ignore it */
|
if((address&0xffff0000)==(DBG_REGS&0xffff0000)){
|
if((address&0xffff0000)==(DBG_REGS&0xffff0000)){
|
printf("DEBUG REG[%04x]=%08x\n", address & 0xffff, value);
|
debug_reg_write(s, address, value);
|
return;
|
return;
|
}
|
}
|
|
|
switch(address){
|
switch(address){
|
case UART_WRITE:
|
case UART_WRITE:
|
Line 826... |
Line 841... |
|
|
uint32_t count_leading(uint32_t lead, uint32_t src){
|
uint32_t count_leading(uint32_t lead, uint32_t src){
|
uint32_t mask, bit_val, i;
|
uint32_t mask, bit_val, i;
|
|
|
mask = 0x80000000;
|
mask = 0x80000000;
|
bit_val = lead? mask : 0x0;
|
bit_val = lead? 0xffffffff : 0x00000000;
|
|
|
for(i=0;i<32;i++){
|
for(i=0;i<32;i++){
|
if((src & mask) != bit_val){
|
if((src & mask) != (bit_val & mask)){
|
return i;
|
return i;
|
}
|
}
|
mask = mask >> 1;
|
mask = mask >> 1;
|
}
|
}
|
|
|
Line 935... |
Line 950... |
/* load delay slot not simulated */
|
/* load delay slot not simulated */
|
log_read(s, addr, data, 1, 1);
|
log_read(s, addr, data, 1, 1);
|
s->r[rt] = data;
|
s->r[rt] = data;
|
}
|
}
|
|
|
|
void process_traps(t_state *s, uint32_t epc, uint32_t rSave, uint32_t rt){
|
|
int32_t i, cause= -1;
|
|
|
|
if(s->trap_cause>=0){
|
|
/* If there is a software-triggered trap pending, deal with it */
|
|
cause = s->trap_cause;
|
|
}
|
|
else{
|
|
/* If there's any hardware interrupt pending, deal with it */
|
|
for(i=0;i<NUM_HW_IRQS;i++){
|
|
if(s->t.irq_trigger_countdown[i]==0){
|
|
/* trigger interrupt i */
|
|
/* FIXME handle irq mask(s) in SR */
|
|
cause = 0; /* cause = hardware interrupt */
|
|
s->t.irq_trigger_countdown[i]--;
|
|
}
|
|
else if (s->t.irq_trigger_countdown[i]>0){
|
|
s->t.irq_trigger_countdown[i]--;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now, whatever the cause was, do the trap handling */
|
|
if(cause >= 0){
|
|
s->trap_cause = cause;
|
|
s->r[rt] = rSave; /* 'undo' current instruction (?) */
|
|
/* set cause field ... */
|
|
s->cp0_cause = (s->delay_slot & 0x1) << 31 | (s->trap_cause & 0x1f) << 2;
|
|
/* ...save previous KU/IE flags in SR... */
|
|
s->status = (s->status & 0xffffffc3) | ((s->status & 0x0f) << 2);
|
|
/* ...and raise KU(EXL) kernel mode flag */
|
|
s->status |= 0x02;
|
|
/* adjust epc if we (i.e. the victim instruction) are in a delay slot */
|
|
if(s->delay_slot){
|
|
epc = epc - 4;
|
|
}
|
|
s->epc = epc;
|
|
s->pc_next = VECTOR_TRAP;
|
|
s->skip = 1;
|
|
s->userMode = 0;
|
|
}
|
|
}
|
|
|
/** Execute one cycle of the CPU (including any interlock stall cycles) */
|
/** Execute one cycle of the CPU (including any interlock stall cycles) */
|
void cycle(t_state *s, int show_mode){
|
void cycle(t_state *s, int show_mode){
|
unsigned int opcode;
|
unsigned int opcode;
|
int delay_slot = 0; /* 1 of this instruction is a branch */
|
int delay_slot = 0; /* 1 of this instruction is a branch */
|
unsigned int op, rs, rt, rd, re, func, imm, target;
|
unsigned int op, rs, rt, rd, re, func, imm, target;
|
Line 950... |
Line 1008... |
char format;
|
char format;
|
uint32_t aux;
|
uint32_t aux;
|
uint32_t target_offset16;
|
uint32_t target_offset16;
|
uint32_t target_long;
|
uint32_t target_long;
|
|
|
|
s->instruction_ctr++;
|
s->trap_cause = -1;
|
s->trap_cause = -1;
|
|
|
/* fetch and decode instruction */
|
/* fetch and decode instruction */
|
opcode = mem_read(s, 4, s->pc, 0);
|
opcode = mem_read(s, 4, s->pc, 0);
|
|
|
Line 1112... |
Line 1171... |
log_call(s->pc_next, epc); break;
|
log_call(s->pc_next, epc); break;
|
case 0x0a:/*MOVZ*/ if(!r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0a:/*MOVZ*/ if(!r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0b:/*MOVN*/ if(r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0b:/*MOVN*/ if(r[rt]) r[rd]=r[rs]; break; /*IV*/
|
case 0x0c:/*SYSCALL*/ s->trap_cause = 8;
|
case 0x0c:/*SYSCALL*/ s->trap_cause = 8;
|
/*
|
/*
|
FIXME enable when running uClinux
|
//FIXME enable when running uClinux
|
printf("SYSCALL (%08x)\n", s->pc);
|
printf("SYSCALL (%08x)\n", s->pc);
|
*/
|
*/
|
break;
|
break;
|
case 0x0d:/*BREAK*/ s->trap_cause = 9;
|
case 0x0d:/*BREAK*/ s->trap_cause = 9;
|
/*
|
/*
|
Line 1181... |
Line 1240... |
case 0x0d:/*ORI*/ r[rt]=r[rs]|imm; break;
|
case 0x0d:/*ORI*/ r[rt]=r[rs]|imm; break;
|
case 0x0e:/*XORI*/ r[rt]=r[rs]^imm; break;
|
case 0x0e:/*XORI*/ r[rt]=r[rs]^imm; break;
|
case 0x0f:/*LUI*/ r[rt]=(imm<<16); break;
|
case 0x0f:/*LUI*/ r[rt]=(imm<<16); break;
|
case 0x10:/*COP0*/
|
case 0x10:/*COP0*/
|
if(s->status & 0x02){ /* kernel mode? */
|
if(s->status & 0x02){ /* kernel mode? */
|
if((opcode & (1<<23)) == 0){ //move from CP0 (mfc0)
|
if(opcode==0x42000010){ // rfe
|
//printf("mfc0: [SR]=0x%08x @ [0x%08x]\n", s->status, epc);
|
/* FIXME unimplemented yet */
|
|
}
|
|
else if((opcode & (1<<23)) == 0){ //move from CP0 (mfc0)
|
|
//printf("mfc0: [%02d]=0x%08x @ [0x%08x]\n", rd, s->status, epc);
|
switch(rd){
|
switch(rd){
|
case 12: r[rt]=s->status & 0x0000003f; break;
|
case 12: r[rt]=s->status & 0x0000003f; break;
|
case 13: r[rt]=s->cp0_cause; break;
|
case 13: r[rt]=s->cp0_cause; break;
|
case 14: r[rt]=s->epc; break;
|
case 14: r[rt]=s->epc; break;
|
case 15: r[rt]=R3000_ID; break;
|
case 15: r[rt]=R3000_ID; break;
|
default:
|
default:
|
printf("mfc0 [%02d] @ [0x%08x]\n", rt, s->pc);
|
/* FIXME log access to unimplemented CP0 register */
|
|
printf("mfc0 [%02d]->%02d @ [0x%08x]\n", rd, rt,s->pc);
|
break;
|
break;
|
}
|
}
|
}
|
}
|
else{ //move to CP0 (mtc0)
|
else{ //move to CP0 (mtc0)
|
/* FIXME check CF= reg address */
|
/* FIXME check CF= reg address */
|
Line 1323... |
Line 1386... |
}
|
}
|
|
|
/* if there's a delayed load pending, do it now: load reg with memory data*/
|
/* if there's a delayed load pending, do it now: load reg with memory data*/
|
/* load delay slots not simulated */
|
/* load delay slots not simulated */
|
|
|
/* Handle exceptions */
|
/* Handle exceptions, software and hardware */
|
if(s->trap_cause>=0){
|
/* Software-triggered traps have priority over HW interrupts, IIF they
|
r[rt] = rSave;
|
trigger in the same clock cycle. */
|
/* set cause field ... */
|
process_traps(s, epc, rSave, rt);
|
s->cp0_cause = (s->delay_slot & 0x1) << 31 | (s->trap_cause & 0x1f) << 2;
|
|
/* ...save previous KU/IE flags in SR... */
|
|
s->status = (s->status & 0xffffffc3) | ((s->status & 0x0f) << 2);
|
|
/* ...and raise KU(EXL) kernel mode flag */
|
|
s->status |= 0x02;
|
|
/* adjust epc if we (i.e. the victim instruction) are in a delay slot */
|
|
if(s->delay_slot){
|
|
epc = epc - 4;
|
|
}
|
|
s->epc = epc;
|
|
s->pc_next = VECTOR_TRAP;
|
|
s->skip = 1;
|
|
s->userMode = 0;
|
|
//s->wakeup = 1;
|
|
}
|
|
|
|
/* if we're NOT showing output to console, log state of CPU to file */
|
/* if we're NOT showing output to console, log state of CPU to file */
|
if(!show_mode){
|
if(!show_mode){
|
s->wakeup |= log_cycle(s);
|
s->wakeup |= log_cycle(s);
|
}
|
}
|
|
|
|
|
|
|
/* if this instruction was any kind of branch that actually jumped, then
|
/* if this instruction was any kind of branch that actually jumped, then
|
the next instruction will be in a delay slot. Remember it. */
|
the next instruction will be in a delay slot. Remember it. */
|
delay_slot = ((lbranch==1) || branch || delay_slot);
|
delay_slot = ((lbranch==1) || branch || delay_slot);
|
s->delay_slot = delay_slot;
|
s->delay_slot = delay_slot;
|
}
|
}
|
Line 1914... |
Line 1960... |
s->blocks[i].mem = NULL;
|
s->blocks[i].mem = NULL;
|
}
|
}
|
}
|
}
|
|
|
void reset_cpu(t_state *s){
|
void reset_cpu(t_state *s){
|
|
uint32_t i;
|
|
|
s->pc = cmd_line_args.start_addr; /* reset start vector or cmd line address */
|
s->pc = cmd_line_args.start_addr; /* reset start vector or cmd line address */
|
s->delay_slot = 0;
|
s->delay_slot = 0;
|
s->failed_assertions = 0; /* no failed assertions pending */
|
s->failed_assertions = 0; /* no failed assertions pending */
|
s->status = 0x02; /* kernel mode, interrupts disabled */
|
s->status = 0x02; /* kernel mode, interrupts disabled */
|
|
s->instruction_ctr = 0;
|
|
for(i=0;i<NUM_HW_IRQS;i++){
|
|
s->t.irq_trigger_countdown[i] = -1;
|
|
}
|
/* init trace struct to prevent spurious logs */
|
/* init trace struct to prevent spurious logs */
|
s->t.status = s->status;
|
s->t.status = s->status;
|
}
|
}
|
|
|
/* FIXME redundant function, merge with reserved_opcode */
|
/* FIXME redundant function, merge with reserved_opcode */
|