| /* 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; |
| } |