diff options
Diffstat (limited to 'sim/lm32/dv-lm32cpu.c')
-rw-r--r-- | sim/lm32/dv-lm32cpu.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/sim/lm32/dv-lm32cpu.c b/sim/lm32/dv-lm32cpu.c new file mode 100644 index 0000000..a81d7d8 --- /dev/null +++ b/sim/lm32/dv-lm32cpu.c @@ -0,0 +1,247 @@ +/* Lattice Mico32 CPU model. + Contributed by Jon Beniston <jon@beniston.com> + + Copyright (C) 2009 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "hw-main.h" +#include "sim-main.h" + + +struct lm32cpu +{ + struct hw_event *event; +}; + +/* input port ID's. */ + +enum +{ + INT0_PORT, + INT1_PORT, + INT2_PORT, + INT3_PORT, + INT4_PORT, + INT5_PORT, + INT6_PORT, + INT7_PORT, + INT8_PORT, + INT9_PORT, + INT10_PORT, + INT11_PORT, + INT12_PORT, + INT13_PORT, + INT14_PORT, + INT15_PORT, + INT16_PORT, + INT17_PORT, + INT18_PORT, + INT19_PORT, + INT20_PORT, + INT21_PORT, + INT22_PORT, + INT23_PORT, + INT24_PORT, + INT25_PORT, + INT26_PORT, + INT27_PORT, + INT28_PORT, + INT29_PORT, + INT30_PORT, + INT31_PORT, +}; + +static const struct hw_port_descriptor lm32cpu_ports[] = { + /* interrupt inputs. */ + {"int0", INT0_PORT, 0, input_port,}, + {"int1", INT1_PORT, 0, input_port,}, + {"int2", INT2_PORT, 0, input_port,}, + {"int3", INT3_PORT, 0, input_port,}, + {"int4", INT4_PORT, 0, input_port,}, + {"int5", INT5_PORT, 0, input_port,}, + {"int6", INT6_PORT, 0, input_port,}, + {"int7", INT7_PORT, 0, input_port,}, + {"int8", INT8_PORT, 0, input_port,}, + {"int9", INT9_PORT, 0, input_port,}, + {"int10", INT10_PORT, 0, input_port,}, + {"int11", INT11_PORT, 0, input_port,}, + {"int12", INT12_PORT, 0, input_port,}, + {"int13", INT13_PORT, 0, input_port,}, + {"int14", INT14_PORT, 0, input_port,}, + {"int15", INT15_PORT, 0, input_port,}, + {"int16", INT16_PORT, 0, input_port,}, + {"int17", INT17_PORT, 0, input_port,}, + {"int18", INT18_PORT, 0, input_port,}, + {"int19", INT19_PORT, 0, input_port,}, + {"int20", INT20_PORT, 0, input_port,}, + {"int21", INT21_PORT, 0, input_port,}, + {"int22", INT22_PORT, 0, input_port,}, + {"int23", INT23_PORT, 0, input_port,}, + {"int24", INT24_PORT, 0, input_port,}, + {"int25", INT25_PORT, 0, input_port,}, + {"int26", INT26_PORT, 0, input_port,}, + {"int27", INT27_PORT, 0, input_port,}, + {"int28", INT28_PORT, 0, input_port,}, + {"int29", INT29_PORT, 0, input_port,}, + {"int30", INT30_PORT, 0, input_port,}, + {"int31", INT31_PORT, 0, input_port,}, + {NULL,}, +}; + + + +/* + * Finish off the partially created hw device. Attach our local + * callbacks. Wire up our port names etc. + */ +static hw_port_event_method lm32cpu_port_event; + + +static void +lm32cpu_finish (struct hw *me) +{ + struct lm32cpu *controller; + + controller = HW_ZALLOC (me, struct lm32cpu); + set_hw_data (me, controller); + set_hw_ports (me, lm32cpu_ports); + set_hw_port_event (me, lm32cpu_port_event); + + /* Initialize the pending interrupt flags. */ + controller->event = NULL; +} + + +/* An event arrives on an interrupt port. */ +static unsigned int s_ui_ExtIntrs = 0; + + +static void +deliver_lm32cpu_interrupt (struct hw *me, void *data) +{ + static unsigned int ip, im, im_and_ip_result; + struct lm32cpu *controller = hw_data (me); + SIM_DESC sd = hw_system (me); + sim_cpu *cpu = STATE_CPU (sd, 0); /* NB: fix CPU 0. */ + address_word cia = CIA_GET (cpu); + int interrupt = (int) data; + + + HW_TRACE ((me, "interrupt-check event")); + + + /* + * Determine if an external interrupt is active + * and needs to cause an exception. + */ + im = lm32bf_h_csr_get (cpu, LM32_CSR_IM); + ip = lm32bf_h_csr_get (cpu, LM32_CSR_IP); + im_and_ip_result = im & ip; + + + if ((lm32bf_h_csr_get (cpu, LM32_CSR_IE) & 1) && (im_and_ip_result != 0)) + { + /* Save PC in exception address register. */ + lm32bf_h_gr_set (cpu, 30, lm32bf_h_pc_get (cpu)); + /* Restart at interrupt offset in handler exception table. */ + lm32bf_h_pc_set (cpu, + lm32bf_h_csr_get (cpu, + LM32_CSR_EBA) + + LM32_EID_INTERRUPT * 32); + /* Save interrupt enable and then clear. */ + lm32bf_h_csr_set (cpu, LM32_CSR_IE, 0x2); + } + + /* reschedule soon. */ + if (controller->event != NULL) + hw_event_queue_deschedule (me, controller->event); + controller->event = NULL; + + + /* if there are external interrupts, schedule an interrupt-check again. + * NOTE: THIS MAKES IT VERY INEFFICIENT. INSTEAD, TRIGGER THIS + * CHECk_EVENT WHEN THE USER ENABLES IE OR USER MODIFIES IM REGISTERS. + */ + if (s_ui_ExtIntrs != 0) + controller->event = + hw_event_queue_schedule (me, 1, deliver_lm32cpu_interrupt, data); +} + + + +/* Handle an event on one of the CPU's ports. */ +static void +lm32cpu_port_event (struct hw *me, + int my_port, + struct hw *source, int source_port, int level) +{ + struct lm32cpu *controller = hw_data (me); + SIM_DESC sd = hw_system (me); + sim_cpu *cpu = STATE_CPU (sd, 0); /* NB: fix CPU 0. */ + address_word cia = CIA_GET (cpu); + + + HW_TRACE ((me, "interrupt event on port %d, level %d", my_port, level)); + + + + /* + * Activate IP if the interrupt's activated; don't do anything if + * the interrupt's deactivated. + */ + if (level == 1) + { + /* + * save state of external interrupt. + */ + s_ui_ExtIntrs |= (1 << my_port); + + /* interrupt-activated so set IP. */ + lm32bf_h_csr_set (cpu, LM32_CSR_IP, + lm32bf_h_csr_get (cpu, LM32_CSR_IP) | (1 << my_port)); + + /* + * Since interrupt is activated, queue an immediate event + * to check if this interrupt is serviceable. + */ + if (controller->event != NULL) + hw_event_queue_deschedule (me, controller->event); + + + /* + * Queue an immediate event to check if this interrupt must be serviced; + * this will happen after the current instruction is complete. + */ + controller->event = hw_event_queue_schedule (me, + 0, + deliver_lm32cpu_interrupt, + 0); + } + else + { + /* + * save state of external interrupt. + */ + s_ui_ExtIntrs &= ~(1 << my_port); + } +} + + +const struct hw_descriptor dv_lm32cpu_descriptor[] = { + {"lm32cpu", lm32cpu_finish,}, + {NULL}, +}; |