Fix _bfd_elf_find_function so that it can cope with overlapping symbols
[binutils-gdb.git] / sim / ppc / hw_glue.c
blob2af2434b5c3412d13aca664e46f59345324134dc
1 /* This file is part of the program psim.
3 Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #ifndef _HW_GLUE_C_
22 #define _HW_GLUE_C_
24 #include "device_table.h"
27 /* DEVICE
30 glue - glue to interconnect and test interrupts
33 DESCRIPTION
36 The glue device provides two functions. Firstly, it provides a
37 mechanism for inspecting and driving the interrupt net. Secondly,
38 it provides a set of boolean primitives that can be used add
39 combinatorial operations to the interrupt network.
41 Glue devices have a variable number of big endian <<output>>
42 registers. Each host-word size. The registers can be both read
43 and written.
45 Writing a value to an output register causes an interrupt (of the
46 specified level) to be driven on the devices corresponding output
47 interrupt port.
49 Reading an <<output>> register returns either the last value
50 written or the most recently computed value (for that register) as
51 a result of an interrupt ariving (which ever was computed last).
53 At present the following sub device types are available:
55 <<glue>>: In addition to driving its output interrupt port with any
56 value written to an interrupt input port is stored in the
57 corresponding <<output>> register. Such input interrupts, however,
58 are not propogated to an output interrupt port.
60 <<glue-and>>: The bit-wise AND of the interrupt inputs is computed
61 and then both stored in <<output>> register zero and propogated to
62 output interrupt output port zero.
65 PROPERTIES
68 reg = <address> <size> (required)
70 Specify the address (within the parent bus) that this device is to
71 live. The address must be 2048 * sizeof(word) (8k in a 32bit
72 simulation) aligned.
75 interrupt-ranges = <int-number> <range> (optional)
77 If present, this specifies the number of valid interrupt inputs (up
78 to the maximum of 2048). By default, <<int-number>> is zero and
79 range is determined by the <<reg>> size.
82 EXAMPLES
85 Enable tracing of the device:
87 | -t glue-device \
90 Create source, bitwize-and, and sink glue devices. Since the
91 device at address <<0x10000>> is of size <<8>> it will have two
92 output interrupt ports.
94 | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \
95 | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \
96 | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \
97 | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \
100 Wire the two source interrupts to the AND device:
102 | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \
103 | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \
106 Wire the AND device up to the sink so that the and's output is not
107 left open.
109 | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \
112 With the above configuration. The client program is able to
113 compute a two bit AND. For instance the <<C>> stub below prints 1
114 AND 0.
116 | unsigned *input = (void*)0xf0010000;
117 | unsigned *output = (void*)0xf0030000;
118 | unsigned ans;
119 | input[0] = htonl(1);
120 | input[1] = htonl(0);
121 | ans = ntohl(*output);
122 | write_string("AND is ");
123 | write_int(ans);
124 | write_line();
127 BUGS
130 A future implementation of this device may support multiple
131 interrupt ranges.
133 Some of the devices listed may not yet be fully implemented.
135 Additional devices such as a dff, an inverter or a latch may be
136 useful.
141 enum {
142 max_nr_interrupts = 2048,
145 typedef enum _hw_glue_type {
146 glue_undefined = 0,
147 glue_io,
148 glue_and,
149 glue_nand,
150 glue_or,
151 glue_xor,
152 glue_nor,
153 glue_not,
154 } hw_glue_type;
156 typedef struct _hw_glue_device {
157 hw_glue_type type;
158 int int_number;
159 int *input;
160 int nr_inputs;
161 unsigned sizeof_input;
162 /* our output registers */
163 int space;
164 unsigned_word address;
165 unsigned sizeof_output;
166 int *output;
167 int nr_outputs;
168 } hw_glue_device;
171 static void
172 hw_glue_init_address(device *me)
174 hw_glue_device *glue = (hw_glue_device*)device_data(me);
176 /* attach to my parent */
177 generic_device_init_address(me);
179 /* establish the output registers */
180 if (glue->output != NULL) {
181 memset(glue->output, 0, glue->sizeof_output);
183 else {
184 reg_property_spec unit;
185 int reg_nr;
186 /* find a relevant reg entry */
187 reg_nr = 0;
188 while (device_find_reg_array_property(me, "reg", reg_nr, &unit)
189 && !device_size_to_attach_size(device_parent(me), &unit.size,
190 &glue->sizeof_output, me))
191 reg_nr++;
192 /* check out the size */
193 if (glue->sizeof_output == 0)
194 device_error(me, "at least one reg property size must be nonzero");
195 if (glue->sizeof_output % sizeof(unsigned_word) != 0)
196 device_error(me, "reg property size must be %zu aligned", sizeof(unsigned_word));
197 /* and the address */
198 device_address_to_attach_address(device_parent(me),
199 &unit.address, &glue->space, &glue->address,
200 me);
201 if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0)
202 device_error(me, "reg property address must be %zu aligned",
203 sizeof(unsigned_word) * max_nr_interrupts);
204 glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word);
205 glue->output = zalloc(glue->sizeof_output);
208 /* establish the input interrupt ports */
209 if (glue->input != NULL) {
210 memset(glue->input, 0, glue->sizeof_input);
212 else {
213 const device_property *ranges = device_find_property(me, "interrupt-ranges");
214 if (ranges == NULL) {
215 glue->int_number = 0;
216 glue->nr_inputs = glue->nr_outputs;
218 else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) {
219 device_error(me, "invalid interrupt-ranges property (incorrect size)");
221 else {
222 const unsigned_cell *int_range = ranges->array;
223 glue->int_number = BE2H_cell(int_range[0]);
224 glue->nr_inputs = BE2H_cell(int_range[1]);
226 glue->sizeof_input = glue->nr_inputs * sizeof(unsigned);
227 glue->input = zalloc(glue->sizeof_input);
230 /* determine our type */
231 if (glue->type == glue_undefined) {
232 const char *name = device_name(me);
233 if (strcmp(name, "glue") == 0)
234 glue->type = glue_io;
235 else if (strcmp(name, "glue-and") == 0)
236 glue->type = glue_and;
237 else
238 device_error(me, "unimplemented glue type");
241 DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n",
242 glue->int_number, glue->nr_inputs, glue->nr_outputs));
245 static unsigned
246 hw_glue_io_read_buffer_callback(device *me,
247 void *dest,
248 int space,
249 unsigned_word addr,
250 unsigned nr_bytes,
251 cpu *processor,
252 unsigned_word cia)
254 hw_glue_device *glue = (hw_glue_device*)device_data(me);
255 int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs;
256 if (nr_bytes != sizeof(unsigned_word)
257 || (addr % sizeof(unsigned_word)) != 0)
258 device_error(me, "missaligned read access (%d:0x%lx:%d) not supported",
259 space, (unsigned long)addr, nr_bytes);
260 *(unsigned_word*)dest = H2BE_4(glue->output[reg]);
261 DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n",
262 reg, (unsigned long) addr, glue->output[reg]));
263 return nr_bytes;
267 static unsigned
268 hw_glue_io_write_buffer_callback(device *me,
269 const void *source,
270 int space,
271 unsigned_word addr,
272 unsigned nr_bytes,
273 cpu *processor,
274 unsigned_word cia)
276 hw_glue_device *glue = (hw_glue_device*)device_data(me);
277 int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts;
278 if (nr_bytes != sizeof(unsigned_word)
279 || (addr % sizeof(unsigned_word)) != 0)
280 device_error(me, "missaligned write access (%d:0x%lx:%d) not supported",
281 space, (unsigned long)addr, nr_bytes);
282 glue->output[reg] = H2BE_4(*(unsigned_word*)source);
283 DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n",
284 reg, (unsigned long) addr, glue->output[reg]));
285 device_interrupt_event(me, reg, glue->output[reg], processor, cia);
286 return nr_bytes;
289 static void
290 hw_glue_interrupt_event(device *me,
291 int my_port,
292 device *source,
293 int source_port,
294 int level,
295 cpu *processor,
296 unsigned_word cia)
298 hw_glue_device *glue = (hw_glue_device*)device_data(me);
299 int i;
300 if (my_port < glue->int_number
301 || my_port >= glue->int_number + glue->nr_inputs)
302 device_error(me, "interrupt %d outside of valid range", my_port);
303 glue->input[my_port - glue->int_number] = level;
304 switch (glue->type) {
305 case glue_io:
307 int port = my_port % glue->nr_outputs;
308 glue->output[port] = level;
309 DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n",
310 my_port,
311 (unsigned long)glue->address + port * sizeof(unsigned_word),
312 level));
313 break;
315 case glue_and:
316 glue->output[0] = glue->input[0];
317 for (i = 1; i < glue->nr_inputs; i++)
318 glue->output[0] &= glue->input[i];
319 DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n",
320 my_port, level, glue->output[0]));
321 device_interrupt_event(me, 0, glue->output[0], processor, cia);
322 break;
323 default:
324 device_error(me, "operator not implemented");
325 break;
330 static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = {
331 { "int", 0, max_nr_interrupts },
332 { NULL }
336 static device_callbacks const hw_glue_callbacks = {
337 { hw_glue_init_address, NULL },
338 { NULL, }, /* address */
339 { hw_glue_io_read_buffer_callback,
340 hw_glue_io_write_buffer_callback, },
341 { NULL, }, /* DMA */
342 { hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */
343 { NULL, }, /* unit */
344 NULL, /* instance */
348 static void *
349 hw_glue_create(const char *name,
350 const device_unit *unit_address,
351 const char *args)
353 /* create the descriptor */
354 hw_glue_device *glue = ZALLOC(hw_glue_device);
355 return glue;
359 const device_descriptor hw_glue_device_descriptor[] = {
360 { "glue", hw_glue_create, &hw_glue_callbacks },
361 { "glue-and", hw_glue_create, &hw_glue_callbacks },
362 { "glue-nand", hw_glue_create, &hw_glue_callbacks },
363 { "glue-or", hw_glue_create, &hw_glue_callbacks },
364 { "glue-xor", hw_glue_create, &hw_glue_callbacks },
365 { "glue-nor", hw_glue_create, &hw_glue_callbacks },
366 { "glue-not", hw_glue_create, &hw_glue_callbacks },
367 { NULL },
370 #endif /* _HW_GLUE_C_ */