| /* The common simulator framework for GDB, the GNU Debugger. |
| |
| Copyright 2002-2021 Free Software Foundation, Inc. |
| |
| Contributed by Andrew Cagney and Red Hat. |
| |
| 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 <string.h> |
| |
| /* DEVICE |
| |
| |
| glue - glue to interconnect and test hardware ports |
| |
| |
| DESCRIPTION |
| |
| |
| The glue device provides two functions. Firstly, it provides a |
| mechanism for inspecting and driving the port network. Secondly, |
| it provides a set of boolean primitives that can be used to apply |
| combinatorial operations to the port network. |
| |
| Glue devices have a variable number of big endian <<output>> |
| registers. Each register is target-word sized. The registers can |
| be read and written. |
| |
| Writing to an output register results in an event being driven |
| (level determined by the value written) on the devices |
| corresponding output port. |
| |
| Reading an <<output>> register returns either the last value |
| written or the most recently computed value (for that register) as |
| a result of an event ariving on that port (which ever was computed |
| last). |
| |
| At present the following sub device types are available: |
| |
| <<glue>>: In addition to driving its output interrupt port with any |
| value written to an interrupt input port is stored in the |
| corresponding <<output>> register. Such input interrupts, however, |
| are not propogated to an output interrupt port. |
| |
| <<glue-and>>: The bit-wise AND of the interrupt inputs is computed |
| and then both stored in <<output>> register zero and propogated to |
| output interrupt output port zero. |
| |
| |
| PROPERTIES |
| |
| |
| reg = <address> <size> (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 = <int-number> <range> (optional) |
| |
| If present, this specifies the number of valid interrupt inputs (up |
| to the maximum of 2048). By default, <<int-number>> is zero and |
| range is determined by the <<reg>> size. |
| |
| |
| PORTS |
| |
| |
| int[0..] (input, output) |
| |
| Both an input and an output port. |
| |
| |
| 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 <<C>> 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 D flip-flop (DFF), an inverter (INV) |
| or a latch (LAT) may prove useful. |
| |
| */ |
| |
| |
| enum |
| { |
| max_nr_ports = 2048, |
| }; |
| |
| enum hw_glue_type |
| { |
| glue_undefined = 0, |
| glue_io, |
| glue_and, |
| glue_nand, |
| glue_or, |
| glue_xor, |
| glue_nor, |
| glue_not, |
| }; |
| |
| struct hw_glue |
| { |
| enum 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; |
| }; |
| |
| |
| static hw_io_read_buffer_method hw_glue_io_read_buffer; |
| static hw_io_write_buffer_method hw_glue_io_write_buffer; |
| static hw_port_event_method hw_glue_port_event; |
| static const struct hw_port_descriptor hw_glue_ports[]; |
| |
| static void |
| hw_glue_finish (struct hw *me) |
| { |
| struct hw_glue *glue = HW_ZALLOC (me, struct hw_glue); |
| const char *name = hw_name (me); |
| |
| /* establish our own methods */ |
| set_hw_data (me, glue); |
| set_hw_io_read_buffer (me, hw_glue_io_read_buffer); |
| set_hw_io_write_buffer (me, hw_glue_io_write_buffer); |
| set_hw_ports (me, hw_glue_ports); |
| set_hw_port_event (me, hw_glue_port_event); |
| |
| /* attach to our parent bus */ |
| do_hw_attach_regs (me); |
| |
| /* establish the output registers */ |
| if (hw_find_property (me, "reg")) |
| { |
| reg_property_spec unit; |
| int reg_nr; |
| |
| /* Find a relevant reg entry. */ |
| reg_nr = 0; |
| while (hw_find_reg_array_property (me, "reg", reg_nr, &unit) |
| && !hw_unit_size_to_attach_size (hw_parent (me), |
| &unit.size, |
| &glue->sizeof_output, |
| me)) |
| reg_nr++; |
| |
| /* Check out the size ... */ |
| if (glue->sizeof_output == 0) |
| hw_abort (me, "at least one reg property size must be nonzero"); |
| if (glue->sizeof_output % sizeof (unsigned_word) != 0) |
| hw_abort (me, "reg property size must be %ld aligned", |
| (long) sizeof (unsigned_word)); |
| |
| /* ... and the address. */ |
| hw_unit_address_to_attach_address (hw_parent (me), |
| &unit.address, |
| &glue->space, |
| &glue->address, |
| me); |
| if (glue->address % (sizeof (unsigned_word) * max_nr_ports) != 0) |
| hw_abort (me, "reg property address must be %ld aligned", |
| (long) (sizeof (unsigned_word) * max_nr_ports)); |
| |
| glue->nr_outputs = glue->sizeof_output / sizeof (unsigned_word); |
| } |
| else |
| { |
| /* Allow bitwise glue devices to declare only ports. */ |
| if (!strcmp (name, "glue")) |
| hw_abort (me, "Missing \"reg\" property"); |
| |
| glue->nr_outputs = 1; |
| glue->sizeof_output = sizeof (unsigned_word); |
| } |
| glue->output = hw_zalloc (me, glue->sizeof_output); |
| |
| /* establish the input ports */ |
| { |
| const struct hw_property *ranges; |
| |
| ranges = hw_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) |
| { |
| hw_abort (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 = hw_zalloc (me, glue->sizeof_input); |
| } |
| |
| /* determine our type */ |
| if (strcmp (name, "glue") == 0) |
| glue->type = glue_io; |
| else if (strcmp (name, "glue-and") == 0) |
| glue->type = glue_and; |
| else if (strcmp (name, "glue-or") == 0) |
| glue->type = glue_or; |
| else if (strcmp (name, "glue-xor") == 0) |
| glue->type = glue_xor; |
| else |
| hw_abort (me, "unimplemented glue type"); |
| |
| HW_TRACE ((me, "int-number %d, nr_inputs %d, nr_outputs %d", |
| glue->int_number, glue->nr_inputs, glue->nr_outputs)); |
| } |
| |
| static unsigned |
| hw_glue_io_read_buffer (struct hw *me, |
| void *dest, |
| int space, |
| unsigned_word addr, |
| unsigned nr_bytes) |
| { |
| struct hw_glue *glue = (struct hw_glue *) hw_data (me); |
| int reg = ((addr - glue->address) / sizeof (unsigned_word)) % glue->nr_outputs; |
| |
| if (nr_bytes != sizeof (unsigned_word) |
| || (addr % sizeof (unsigned_word)) != 0) |
| hw_abort (me, "missaligned read access (%d:0x%lx:%d) not supported", |
| space, (unsigned long)addr, nr_bytes); |
| |
| *(unsigned_word *)dest = H2BE_4 (glue->output[reg]); |
| |
| HW_TRACE ((me, "read - port %d (0x%lx), level %d", |
| reg, (unsigned long) addr, glue->output[reg])); |
| |
| return nr_bytes; |
| } |
| |
| |
| static unsigned |
| hw_glue_io_write_buffer (struct hw *me, |
| const void *source, |
| int space, |
| unsigned_word addr, |
| unsigned nr_bytes) |
| { |
| struct hw_glue *glue = (struct hw_glue *) hw_data (me); |
| int reg = ((addr - glue->address) / sizeof (unsigned_word)) % max_nr_ports; |
| |
| if (nr_bytes != sizeof (unsigned_word) |
| || (addr % sizeof (unsigned_word)) != 0) |
| hw_abort (me, "missaligned write access (%d:0x%lx:%d) not supported", |
| space, (unsigned long) addr, nr_bytes); |
| |
| glue->output[reg] = H2BE_4 (*(unsigned_word *)source); |
| |
| HW_TRACE ((me, "write - port %d (0x%lx), level %d", |
| reg, (unsigned long) addr, glue->output[reg])); |
| |
| hw_port_event (me, reg, glue->output[reg]); |
| |
| return nr_bytes; |
| } |
| |
| static void |
| hw_glue_port_event (struct hw *me, |
| int my_port, |
| struct hw *source, |
| int source_port, |
| int level) |
| { |
| struct hw_glue *glue = (struct hw_glue *) hw_data (me); |
| int i; |
| |
| if (my_port < glue->int_number |
| || my_port >= glue->int_number + glue->nr_inputs) |
| hw_abort (me, "port %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; |
| |
| HW_TRACE ((me, "input - port %d (0x%lx), level %d", |
| my_port, |
| (unsigned long) glue->address + port * sizeof (unsigned_word), |
| level)); |
| return; |
| } |
| case glue_and: |
| { |
| glue->output[0] = glue->input[0]; |
| for (i = 1; i < glue->nr_inputs; i++) |
| glue->output[0] &= glue->input[i]; |
| break; |
| } |
| case glue_or: |
| { |
| glue->output[0] = glue->input[0]; |
| for (i = 1; i < glue->nr_inputs; i++) |
| glue->output[0] |= glue->input[i]; |
| break; |
| } |
| case glue_xor: |
| { |
| glue->output[0] = glue->input[0]; |
| for (i = 1; i < glue->nr_inputs; i++) |
| glue->output[0] ^= glue->input[i]; |
| break; |
| } |
| default: |
| { |
| hw_abort (me, "operator not implemented"); |
| return; |
| } |
| } |
| |
| /* If we fell through, we want to generate a port event. */ |
| HW_TRACE ((me, "port %d, level %d arrived - output %d", |
| my_port, level, glue->output[0])); |
| |
| hw_port_event (me, 0, glue->output[0]); |
| } |
| |
| |
| static const struct hw_port_descriptor hw_glue_ports[] = |
| { |
| { "int", 0, max_nr_ports, 0 }, |
| { NULL, 0, 0, 0 } |
| }; |
| |
| |
| const struct hw_descriptor dv_glue_descriptor[] = |
| { |
| { "glue", hw_glue_finish, }, |
| { "glue-and", hw_glue_finish, }, |
| { "glue-nand", hw_glue_finish, }, |
| { "glue-or", hw_glue_finish, }, |
| { "glue-xor", hw_glue_finish, }, |
| { "glue-nor", hw_glue_finish, }, |
| { "glue-not", hw_glue_finish, }, |
| { NULL, NULL }, |
| }; |