| /* This must come before any other includes. */ | |
| #include "defs.h" | |
| #include "sim-main.h" | |
| #include "sim-options.h" | |
| #include "v850-sim.h" | |
| #include "sim-assert.h" | |
| #include "itable.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include "bfd.h" | |
| #include "target-newlib-syscall.h" | |
| static const char * get_insn_name (sim_cpu *, int); | |
| /* For compatibility. */ | |
| SIM_DESC simulator; | |
| /* V850 interrupt model. */ | |
| enum interrupt_type | |
| { | |
| int_reset, | |
| int_nmi, | |
| int_intov1, | |
| int_intp10, | |
| int_intp11, | |
| int_intp12, | |
| int_intp13, | |
| int_intcm4, | |
| num_int_types | |
| }; | |
| const char *interrupt_names[] = | |
| { | |
| "reset", | |
| "nmi", | |
| "intov1", | |
| "intp10", | |
| "intp11", | |
| "intp12", | |
| "intp13", | |
| "intcm4", | |
| NULL | |
| }; | |
| static void | |
| do_interrupt (SIM_DESC sd, void *data) | |
| { | |
| sim_cpu *cpu = STATE_CPU (sd, 0); | |
| struct v850_sim_cpu *v850_cpu = V850_SIM_CPU (cpu); | |
| const char **interrupt_name = (const char**)data; | |
| enum interrupt_type inttype; | |
| inttype = (interrupt_name - STATE_WATCHPOINTS (sd)->interrupt_names); | |
| /* For a hardware reset, drop everything and jump to the start | |
| address */ | |
| if (inttype == int_reset) | |
| { | |
| PC = 0; | |
| PSW = 0x20; | |
| ECR = 0; | |
| sim_engine_restart (sd, NULL, NULL, NULL_CIA); | |
| } | |
| /* Deliver an NMI when allowed */ | |
| if (inttype == int_nmi) | |
| { | |
| if (PSW & PSW_NP) | |
| { | |
| /* We're already working on an NMI, so this one must wait | |
| around until the previous one is done. The processor | |
| ignores subsequent NMIs, so we don't need to count them. | |
| Just keep re-scheduling a single NMI until it manages to | |
| be delivered */ | |
| if (v850_cpu->pending_nmi != NULL) | |
| sim_events_deschedule (sd, v850_cpu->pending_nmi); | |
| v850_cpu->pending_nmi = | |
| sim_events_schedule (sd, 1, do_interrupt, data); | |
| return; | |
| } | |
| else | |
| { | |
| /* NMI can be delivered. Do not deschedule pending_nmi as | |
| that, if still in the event queue, is a second NMI that | |
| needs to be delivered later. */ | |
| FEPC = PC; | |
| FEPSW = PSW; | |
| /* Set the FECC part of the ECR. */ | |
| ECR &= 0x0000ffff; | |
| ECR |= 0x10; | |
| PSW |= PSW_NP; | |
| PSW &= ~PSW_EP; | |
| PSW |= PSW_ID; | |
| PC = 0x10; | |
| sim_engine_restart (sd, NULL, NULL, NULL_CIA); | |
| } | |
| } | |
| /* deliver maskable interrupt when allowed */ | |
| if (inttype > int_nmi && inttype < num_int_types) | |
| { | |
| if ((PSW & PSW_NP) || (PSW & PSW_ID)) | |
| { | |
| /* Can't deliver this interrupt, reschedule it for later */ | |
| sim_events_schedule (sd, 1, do_interrupt, data); | |
| return; | |
| } | |
| else | |
| { | |
| /* save context */ | |
| EIPC = PC; | |
| EIPSW = PSW; | |
| /* Disable further interrupts. */ | |
| PSW |= PSW_ID; | |
| /* Indicate that we're doing interrupt not exception processing. */ | |
| PSW &= ~PSW_EP; | |
| /* Clear the EICC part of the ECR, will set below. */ | |
| ECR &= 0xffff0000; | |
| switch (inttype) | |
| { | |
| case int_intov1: | |
| PC = 0x80; | |
| ECR |= 0x80; | |
| break; | |
| case int_intp10: | |
| PC = 0x90; | |
| ECR |= 0x90; | |
| break; | |
| case int_intp11: | |
| PC = 0xa0; | |
| ECR |= 0xa0; | |
| break; | |
| case int_intp12: | |
| PC = 0xb0; | |
| ECR |= 0xb0; | |
| break; | |
| case int_intp13: | |
| PC = 0xc0; | |
| ECR |= 0xc0; | |
| break; | |
| case int_intcm4: | |
| PC = 0xd0; | |
| ECR |= 0xd0; | |
| break; | |
| default: | |
| /* Should never be possible. */ | |
| sim_engine_abort (sd, NULL, NULL_CIA, | |
| "do_interrupt - internal error - bad switch"); | |
| break; | |
| } | |
| } | |
| sim_engine_restart (sd, NULL, NULL, NULL_CIA); | |
| } | |
| /* some other interrupt? */ | |
| sim_engine_abort (sd, NULL, NULL_CIA, | |
| "do_interrupt - internal error - interrupt %d unknown", | |
| inttype); | |
| } | |
| /* Return name of an insn, used by insn profiling. */ | |
| static const char * | |
| get_insn_name (sim_cpu *cpu, int i) | |
| { | |
| return itable[i].name; | |
| } | |
| /* These default values correspond to expected usage for the chip. */ | |
| uint32_t OP[4]; | |
| static sim_cia | |
| v850_pc_get (sim_cpu *cpu) | |
| { | |
| return PC; | |
| } | |
| static void | |
| v850_pc_set (sim_cpu *cpu, sim_cia pc) | |
| { | |
| PC = pc; | |
| } | |
| static int v850_reg_fetch (SIM_CPU *, int, void *, int); | |
| static int v850_reg_store (SIM_CPU *, int, const void *, int); | |
| SIM_DESC | |
| sim_open (SIM_OPEN_KIND kind, | |
| host_callback * cb, | |
| struct bfd * abfd, | |
| char * const * argv) | |
| { | |
| int i; | |
| SIM_DESC sd = sim_state_alloc (kind, cb); | |
| int mach; | |
| SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER); | |
| /* Set default options before parsing user options. */ | |
| current_target_byte_order = BFD_ENDIAN_LITTLE; | |
| cb->syscall_map = cb_v850_syscall_map; | |
| /* The cpu data is kept in a separately allocated chunk of memory. */ | |
| if (sim_cpu_alloc_all_extra (sd, 0, sizeof (struct v850_sim_cpu)) | |
| != SIM_RC_OK) | |
| return 0; | |
| /* for compatibility */ | |
| simulator = sd; | |
| /* FIXME: should be better way of setting up interrupts */ | |
| STATE_WATCHPOINTS (sd)->interrupt_handler = do_interrupt; | |
| STATE_WATCHPOINTS (sd)->interrupt_names = interrupt_names; | |
| /* Initialize the mechanism for doing insn profiling. */ | |
| CPU_INSN_NAME (STATE_CPU (sd, 0)) = get_insn_name; | |
| CPU_MAX_INSNS (STATE_CPU (sd, 0)) = nr_itable_entries; | |
| if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK) | |
| return 0; | |
| /* Allocate core managed memory */ | |
| /* "Mirror" the ROM addresses below 1MB. */ | |
| sim_do_commandf (sd, "memory region 0,0x100000,0x%x", V850_ROM_SIZE); | |
| /* Chunk of ram adjacent to rom */ | |
| sim_do_commandf (sd, "memory region 0x100000,0x%x", V850_LOW_END-0x100000); | |
| /* peripheral I/O region - mirror 1K across 4k (0x1000) */ | |
| sim_do_command (sd, "memory region 0xfff000,0x1000,1024"); | |
| /* similarly if in the internal RAM region */ | |
| sim_do_command (sd, "memory region 0xffe000,0x1000,1024"); | |
| /* The parser will print an error message for us, so we silently return. */ | |
| if (sim_parse_args (sd, argv) != SIM_RC_OK) | |
| { | |
| /* Uninstall the modules to avoid memory leaks, | |
| file descriptor leaks, etc. */ | |
| sim_module_uninstall (sd); | |
| return 0; | |
| } | |
| /* check for/establish the a reference program image */ | |
| if (sim_analyze_program (sd, STATE_PROG_FILE (sd), abfd) != SIM_RC_OK) | |
| { | |
| sim_module_uninstall (sd); | |
| return 0; | |
| } | |
| /* establish any remaining configuration options */ | |
| if (sim_config (sd) != SIM_RC_OK) | |
| { | |
| sim_module_uninstall (sd); | |
| return 0; | |
| } | |
| if (sim_post_argv_init (sd) != SIM_RC_OK) | |
| { | |
| /* Uninstall the modules to avoid memory leaks, | |
| file descriptor leaks, etc. */ | |
| sim_module_uninstall (sd); | |
| return 0; | |
| } | |
| /* determine the machine type */ | |
| if (STATE_ARCHITECTURE (sd) != NULL | |
| && (STATE_ARCHITECTURE (sd)->arch == bfd_arch_v850 | |
| || STATE_ARCHITECTURE (sd)->arch == bfd_arch_v850_rh850)) | |
| mach = STATE_ARCHITECTURE (sd)->mach; | |
| else | |
| mach = bfd_mach_v850; /* default */ | |
| /* set machine specific configuration */ | |
| switch (mach) | |
| { | |
| case bfd_mach_v850: | |
| case bfd_mach_v850e: | |
| case bfd_mach_v850e1: | |
| case bfd_mach_v850e2: | |
| case bfd_mach_v850e2v3: | |
| case bfd_mach_v850e3v5: | |
| V850_SIM_CPU (STATE_CPU (sd, 0))->psw_mask = | |
| (PSW_NP | PSW_EP | PSW_ID | PSW_SAT | PSW_CY | PSW_OV | PSW_S | PSW_Z); | |
| break; | |
| } | |
| /* CPU specific initialization. */ | |
| for (i = 0; i < MAX_NR_PROCESSORS; ++i) | |
| { | |
| SIM_CPU *cpu = STATE_CPU (sd, i); | |
| CPU_REG_FETCH (cpu) = v850_reg_fetch; | |
| CPU_REG_STORE (cpu) = v850_reg_store; | |
| CPU_PC_FETCH (cpu) = v850_pc_get; | |
| CPU_PC_STORE (cpu) = v850_pc_set; | |
| } | |
| return sd; | |
| } | |
| SIM_RC | |
| sim_create_inferior (SIM_DESC sd, | |
| struct bfd * prog_bfd, | |
| char * const *argv, | |
| char * const *env) | |
| { | |
| memset (&State, 0, sizeof (State)); | |
| if (prog_bfd != NULL) | |
| PC = bfd_get_start_address (prog_bfd); | |
| return SIM_RC_OK; | |
| } | |
| static int | |
| v850_reg_fetch (SIM_CPU *cpu, int rn, void *memory, int length) | |
| { | |
| *(uint32_t*)memory = H2T_4 (State.regs[rn]); | |
| return -1; | |
| } | |
| static int | |
| v850_reg_store (SIM_CPU *cpu, int rn, const void *memory, int length) | |
| { | |
| State.regs[rn] = T2H_4 (*(uint32_t *) memory); | |
| return length; | |
| } |