/* This file is part of the program psim. Copyright (C) 1994-1996, Andrew Cagney 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 . */ #ifndef _HW_GLUE_C_ #define _HW_GLUE_C_ #include "device_table.h" /* DEVICE glue - glue to interconnect and test interrupts DESCRIPTION The glue device provides two functions. Firstly, it provides a mechanism for inspecting and driving the interrupt net. Secondly, it provides a set of boolean primitives that can be used add combinatorial operations to the interrupt network. Glue devices have a variable number of big endian <> registers. Each host-word size. The registers can be both read and written. Writing a value to an output register causes an interrupt (of the specified level) to be driven on the devices corresponding output interrupt port. Reading an <> register returns either the last value written or the most recently computed value (for that register) as a result of an interrupt ariving (which ever was computed last). At present the following sub device types are available: <>: In addition to driving its output interrupt port with any value written to an interrupt input port is stored in the corresponding <> register. Such input interrupts, however, are not propogated to an output interrupt port. <>: The bit-wise AND of the interrupt inputs is computed and then both stored in <> register zero and propogated to output interrupt output port zero. PROPERTIES reg =
(required) Specify the address (within the parent bus) that this device is to live. The address must be 2048 * sizeof(word) (8k in a 32bit simulation) aligned. interrupt-ranges = (optional) If present, this specifies the number of valid interrupt inputs (up to the maximum of 2048). By default, <> is zero and range is determined by the <> size. EXAMPLES Enable tracing of the device: | -t glue-device \ Create source, bitwize-and, and sink glue devices. Since the device at address <<0x10000>> is of size <<8>> it will have two output interrupt ports. | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \ | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \ | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \ | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \ Wire the two source interrupts to the AND device: | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \ | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \ Wire the AND device up to the sink so that the and's output is not left open. | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \ With the above configuration. The client program is able to compute a two bit AND. For instance the <> stub below prints 1 AND 0. | unsigned *input = (void*)0xf0010000; | unsigned *output = (void*)0xf0030000; | unsigned ans; | input[0] = htonl(1); | input[1] = htonl(0); | ans = ntohl(*output); | write_string("AND is "); | write_int(ans); | write_line(); BUGS A future implementation of this device may support multiple interrupt ranges. Some of the devices listed may not yet be fully implemented. Additional devices such as a dff, an inverter or a latch may be useful. */ enum { max_nr_interrupts = 2048, }; typedef enum _hw_glue_type { glue_undefined = 0, glue_io, glue_and, glue_nand, glue_or, glue_xor, glue_nor, glue_not, } hw_glue_type; typedef struct _hw_glue_device { hw_glue_type type; int int_number; int *input; int nr_inputs; unsigned sizeof_input; /* our output registers */ int space; unsigned_word address; unsigned sizeof_output; int *output; int nr_outputs; } hw_glue_device; static void hw_glue_init_address(device *me) { hw_glue_device *glue = (hw_glue_device*)device_data(me); /* attach to my parent */ generic_device_init_address(me); /* establish the output registers */ if (glue->output != NULL) { memset(glue->output, 0, glue->sizeof_output); } else { reg_property_spec unit; int reg_nr; /* find a relevant reg entry */ reg_nr = 0; while (device_find_reg_array_property(me, "reg", reg_nr, &unit) && !device_size_to_attach_size(device_parent(me), &unit.size, &glue->sizeof_output, me)) reg_nr++; /* check out the size */ if (glue->sizeof_output == 0) device_error(me, "at least one reg property size must be nonzero"); if (glue->sizeof_output % sizeof(unsigned_word) != 0) device_error(me, "reg property size must be %d aligned", sizeof(unsigned_word)); /* and the address */ device_address_to_attach_address(device_parent(me), &unit.address, &glue->space, &glue->address, me); if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0) device_error(me, "reg property address must be %d aligned", sizeof(unsigned_word) * max_nr_interrupts); glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word); glue->output = zalloc(glue->sizeof_output); } /* establish the input interrupt ports */ if (glue->input != NULL) { memset(glue->input, 0, glue->sizeof_input); } else { const device_property *ranges = device_find_property(me, "interrupt-ranges"); if (ranges == NULL) { glue->int_number = 0; glue->nr_inputs = glue->nr_outputs; } else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) { device_error(me, "invalid interrupt-ranges property (incorrect size)"); } else { const unsigned_cell *int_range = ranges->array; glue->int_number = BE2H_cell(int_range[0]); glue->nr_inputs = BE2H_cell(int_range[1]); } glue->sizeof_input = glue->nr_inputs * sizeof(unsigned); glue->input = zalloc(glue->sizeof_input); } /* determine our type */ if (glue->type == glue_undefined) { const char *name = device_name(me); if (strcmp(name, "glue") == 0) glue->type = glue_io; else if (strcmp(name, "glue-and") == 0) glue->type = glue_and; else device_error(me, "unimplemented glue type"); } DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n", glue->int_number, glue->nr_inputs, glue->nr_outputs)); } static unsigned hw_glue_io_read_buffer_callback(device *me, void *dest, int space, unsigned_word addr, unsigned nr_bytes, cpu *processor, unsigned_word cia) { hw_glue_device *glue = (hw_glue_device*)device_data(me); int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs; if (nr_bytes != sizeof(unsigned_word) || (addr % sizeof(unsigned_word)) != 0) device_error(me, "missaligned read access (%d:0x%lx:%d) not supported", space, (unsigned long)addr, nr_bytes); *(unsigned_word*)dest = H2BE_4(glue->output[reg]); DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n", reg, (unsigned long) addr, glue->output[reg])); return nr_bytes; } static unsigned hw_glue_io_write_buffer_callback(device *me, const void *source, int space, unsigned_word addr, unsigned nr_bytes, cpu *processor, unsigned_word cia) { hw_glue_device *glue = (hw_glue_device*)device_data(me); int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts; if (nr_bytes != sizeof(unsigned_word) || (addr % sizeof(unsigned_word)) != 0) device_error(me, "missaligned write access (%d:0x%lx:%d) not supported", space, (unsigned long)addr, nr_bytes); glue->output[reg] = H2BE_4(*(unsigned_word*)source); DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n", reg, (unsigned long) addr, glue->output[reg])); device_interrupt_event(me, reg, glue->output[reg], processor, cia); return nr_bytes; } static void hw_glue_interrupt_event(device *me, int my_port, device *source, int source_port, int level, cpu *processor, unsigned_word cia) { hw_glue_device *glue = (hw_glue_device*)device_data(me); int i; if (my_port < glue->int_number || my_port >= glue->int_number + glue->nr_inputs) device_error(me, "interrupt %d outside of valid range", my_port); glue->input[my_port - glue->int_number] = level; switch (glue->type) { case glue_io: { int port = my_port % glue->nr_outputs; glue->output[port] = level; DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n", my_port, (unsigned long)glue->address + port * sizeof(unsigned_word), level)); break; } case glue_and: glue->output[0] = glue->input[0]; for (i = 1; i < glue->nr_inputs; i++) glue->output[0] &= glue->input[i]; DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n", my_port, level, glue->output[0])); device_interrupt_event(me, 0, glue->output[0], processor, cia); break; default: device_error(me, "operator not implemented"); break; } } static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = { { "int", 0, max_nr_interrupts }, { NULL } }; static device_callbacks const hw_glue_callbacks = { { hw_glue_init_address, NULL }, { NULL, }, /* address */ { hw_glue_io_read_buffer_callback, hw_glue_io_write_buffer_callback, }, { NULL, }, /* DMA */ { hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */ { NULL, }, /* unit */ NULL, /* instance */ }; static void * hw_glue_create(const char *name, const device_unit *unit_address, const char *args) { /* create the descriptor */ hw_glue_device *glue = ZALLOC(hw_glue_device); return glue; } const device_descriptor hw_glue_device_descriptor[] = { { "glue", hw_glue_create, &hw_glue_callbacks }, { "glue-and", hw_glue_create, &hw_glue_callbacks }, { "glue-nand", hw_glue_create, &hw_glue_callbacks }, { "glue-or", hw_glue_create, &hw_glue_callbacks }, { "glue-xor", hw_glue_create, &hw_glue_callbacks }, { "glue-nor", hw_glue_create, &hw_glue_callbacks }, { "glue-not", hw_glue_create, &hw_glue_callbacks }, { NULL }, }; #endif /* _HW_GLUE_C_ */