1/*  This file is part of the program psim.
2
3    Copyright (C) 1994-1996, Andrew Cagney <cagney@highland.com.au>
4
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 2 of the License, or
8    (at your option) any later version.
9
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.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19    */
20
21
22#ifndef _HW_GLUE_C_
23#define _HW_GLUE_C_
24
25#include "device_table.h"
26
27
28/* DEVICE
29
30
31   glue - glue to interconnect and test interrupts
32
33
34   DESCRIPTION
35
36
37   The glue device provides two functions.  Firstly, it provides a
38   mechanism for inspecting and driving the interrupt net.  Secondly,
39   it provides a set of boolean primitives that can be used add
40   combinatorial operations to the interrupt network.
41
42   Glue devices have a variable number of big endian <<output>>
43   registers.  Each host-word size.  The registers can be both read
44   and written.
45
46   Writing a value to an output register causes an interrupt (of the
47   specified level) to be driven on the devices corresponding output
48   interrupt port.
49
50   Reading an <<output>> register returns either the last value
51   written or the most recently computed value (for that register) as
52   a result of an interrupt ariving (which ever was computed last).
53
54   At present the following sub device types are available:
55
56   <<glue>>: In addition to driving its output interrupt port with any
57   value written to an interrupt input port is stored in the
58   corresponding <<output>> register.  Such input interrupts, however,
59   are not propogated to an output interrupt port.
60
61   <<glue-and>>: The bit-wise AND of the interrupt inputs is computed
62   and then both stored in <<output>> register zero and propogated to
63   output interrupt output port zero.
64
65
66   PROPERTIES
67
68
69   reg = <address> <size> (required)
70
71   Specify the address (within the parent bus) that this device is to
72   live.  The address must be 2048 * sizeof(word) (8k in a 32bit
73   simulation) aligned.
74
75
76   interrupt-ranges = <int-number> <range> (optional)
77
78   If present, this specifies the number of valid interrupt inputs (up
79   to the maximum of 2048).  By default, <<int-number>> is zero and
80   range is determined by the <<reg>> size.
81
82
83   EXAMPLES
84
85
86   Enable tracing of the device:
87
88   | -t glue-device \
89
90
91   Create source, bitwize-and, and sink glue devices.  Since the
92   device at address <<0x10000>> is of size <<8>> it will have two
93   output interrupt ports.
94
95   | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \
96   | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \
97   | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \
98   | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \
99
100
101   Wire the two source interrupts to the AND device:
102
103   | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \
104   | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \
105
106
107   Wire the AND device up to the sink so that the and's output is not
108   left open.
109
110   | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \
111
112
113   With the above configuration.  The client program is able to
114   compute a two bit AND.  For instance the <<C>> stub below prints 1
115   AND 0.
116
117   |  unsigned *input = (void*)0xf0010000;
118   |  unsigned *output = (void*)0xf0030000;
119   |  unsigned ans;
120   |  input[0] = htonl(1);
121   |  input[1] = htonl(0);
122   |  ans = ntohl(*output);
123   |  write_string("AND is ");
124   |  write_int(ans);
125   |  write_line();
126
127
128   BUGS
129
130
131   A future implementation of this device may support multiple
132   interrupt ranges.
133
134   Some of the devices listed may not yet be fully implemented.
135
136   Additional devices such as a dff, an inverter or a latch may be
137   useful.
138
139   */
140
141
142enum {
143  max_nr_interrupts = 2048,
144};
145
146typedef enum _hw_glue_type {
147  glue_undefined = 0,
148  glue_io,
149  glue_and,
150  glue_nand,
151  glue_or,
152  glue_xor,
153  glue_nor,
154  glue_not,
155} hw_glue_type;
156
157typedef struct _hw_glue_device {
158  hw_glue_type type;
159  int int_number;
160  int *input;
161  int nr_inputs;
162  unsigned sizeof_input;
163  /* our output registers */
164  int space;
165  unsigned_word address;
166  unsigned sizeof_output;
167  int *output;
168  int nr_outputs;
169} hw_glue_device;
170
171
172static void
173hw_glue_init_address(device *me)
174{
175  hw_glue_device *glue = (hw_glue_device*)device_data(me);
176
177  /* attach to my parent */
178  generic_device_init_address(me);
179
180  /* establish the output registers */
181  if (glue->output != NULL) {
182    memset(glue->output, 0, glue->sizeof_output);
183  }
184  else {
185    reg_property_spec unit;
186    int reg_nr;
187    /* find a relevant reg entry */
188    reg_nr = 0;
189    while (device_find_reg_array_property(me, "reg", reg_nr, &unit)
190	   && !device_size_to_attach_size(device_parent(me), &unit.size,
191					  &glue->sizeof_output, me))
192      reg_nr++;
193    /* check out the size */
194    if (glue->sizeof_output == 0)
195      device_error(me, "at least one reg property size must be nonzero");
196    if (glue->sizeof_output % sizeof(unsigned_word) != 0)
197      device_error(me, "reg property size must be %d aligned", sizeof(unsigned_word));
198    /* and the address */
199    device_address_to_attach_address(device_parent(me),
200				     &unit.address, &glue->space, &glue->address,
201				     me);
202    if (glue->address % (sizeof(unsigned_word) * max_nr_interrupts) != 0)
203      device_error(me, "reg property address must be %d aligned",
204		   sizeof(unsigned_word) * max_nr_interrupts);
205    glue->nr_outputs = glue->sizeof_output / sizeof(unsigned_word);
206    glue->output = zalloc(glue->sizeof_output);
207  }
208
209  /* establish the input interrupt ports */
210  if (glue->input != NULL) {
211    memset(glue->input, 0, glue->sizeof_input);
212  }
213  else {
214    const device_property *ranges = device_find_property(me, "interrupt-ranges");
215    if (ranges == NULL) {
216      glue->int_number = 0;
217      glue->nr_inputs = glue->nr_outputs;
218    }
219    else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) {
220      device_error(me, "invalid interrupt-ranges property (incorrect size)");
221    }
222    else {
223      const unsigned_cell *int_range = ranges->array;
224      glue->int_number = BE2H_cell(int_range[0]);
225      glue->nr_inputs = BE2H_cell(int_range[1]);
226    }
227    glue->sizeof_input = glue->nr_inputs * sizeof(unsigned);
228    glue->input = zalloc(glue->sizeof_input);
229  }
230
231  /* determine our type */
232  if (glue->type == glue_undefined) {
233    const char *name = device_name(me);
234    if (strcmp(name, "glue") == 0)
235      glue->type = glue_io;
236    else if (strcmp(name, "glue-and") == 0)
237      glue->type = glue_and;
238    else
239      device_error(me, "unimplemented glue type");
240  }
241
242  DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n",
243		glue->int_number, glue->nr_inputs, glue->nr_outputs));
244}
245
246static unsigned
247hw_glue_io_read_buffer_callback(device *me,
248				void *dest,
249				int space,
250				unsigned_word addr,
251				unsigned nr_bytes,
252				cpu *processor,
253				unsigned_word cia)
254{
255  hw_glue_device *glue = (hw_glue_device*)device_data(me);
256  int reg = ((addr - glue->address) / sizeof(unsigned_word)) % glue->nr_outputs;
257  if (nr_bytes != sizeof(unsigned_word)
258      || (addr % sizeof(unsigned_word)) != 0)
259     device_error(me, "missaligned read access (%d:0x%lx:%d) not supported",
260		  space, (unsigned long)addr, nr_bytes);
261  *(unsigned_word*)dest = H2BE_4(glue->output[reg]);
262  DTRACE(glue, ("read - interrupt %d (0x%lx), level %d\n",
263		reg, (unsigned long) addr, glue->output[reg]));
264  return nr_bytes;
265}
266
267
268static unsigned
269hw_glue_io_write_buffer_callback(device *me,
270				 const void *source,
271				 int space,
272				 unsigned_word addr,
273				 unsigned nr_bytes,
274				 cpu *processor,
275				 unsigned_word cia)
276{
277  hw_glue_device *glue = (hw_glue_device*)device_data(me);
278  int reg = ((addr - glue->address) / sizeof(unsigned_word)) % max_nr_interrupts;
279  if (nr_bytes != sizeof(unsigned_word)
280      || (addr % sizeof(unsigned_word)) != 0)
281    device_error(me, "missaligned write access (%d:0x%lx:%d) not supported",
282		 space, (unsigned long)addr, nr_bytes);
283  glue->output[reg] = H2BE_4(*(unsigned_word*)source);
284  DTRACE(glue, ("write - interrupt %d (0x%lx), level %d\n",
285		reg, (unsigned long) addr, glue->output[reg]));
286  device_interrupt_event(me, reg, glue->output[reg], processor, cia);
287  return nr_bytes;
288}
289
290static void
291hw_glue_interrupt_event(device *me,
292			int my_port,
293			device *source,
294			int source_port,
295			int level,
296			cpu *processor,
297			unsigned_word cia)
298{
299  hw_glue_device *glue = (hw_glue_device*)device_data(me);
300  int i;
301  if (my_port < glue->int_number
302      || my_port >= glue->int_number + glue->nr_inputs)
303    device_error(me, "interrupt %d outside of valid range", my_port);
304  glue->input[my_port - glue->int_number] = level;
305  switch (glue->type) {
306  case glue_io:
307    {
308      int port = my_port % glue->nr_outputs;
309      glue->output[port] = level;
310      DTRACE(glue, ("input - interrupt %d (0x%lx), level %d\n",
311		    my_port,
312		    (unsigned long)glue->address + port * sizeof(unsigned_word),
313		    level));
314      break;
315    }
316  case glue_and:
317    glue->output[0] = glue->input[0];
318    for (i = 1; i < glue->nr_inputs; i++)
319      glue->output[0] &= glue->input[i];
320    DTRACE(glue, ("and - interrupt %d, level %d arrived - output %d\n",
321		  my_port, level, glue->output[0]));
322    device_interrupt_event(me, 0, glue->output[0], processor, cia);
323    break;
324  default:
325    device_error(me, "operator not implemented");
326    break;
327  }
328}
329
330
331static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = {
332  { "int", 0, max_nr_interrupts },
333  { NULL }
334};
335
336
337static device_callbacks const hw_glue_callbacks = {
338  { hw_glue_init_address, NULL },
339  { NULL, }, /* address */
340  { hw_glue_io_read_buffer_callback,
341      hw_glue_io_write_buffer_callback, },
342  { NULL, }, /* DMA */
343  { hw_glue_interrupt_event, NULL, hw_glue_interrupt_ports }, /* interrupt */
344  { NULL, }, /* unit */
345  NULL, /* instance */
346};
347
348
349static void *
350hw_glue_create(const char *name,
351	      const device_unit *unit_address,
352	      const char *args)
353{
354  /* create the descriptor */
355  hw_glue_device *glue = ZALLOC(hw_glue_device);
356  return glue;
357}
358
359
360const device_descriptor hw_glue_device_descriptor[] = {
361  { "glue", hw_glue_create, &hw_glue_callbacks },
362  { "glue-and", hw_glue_create, &hw_glue_callbacks },
363  { "glue-nand", hw_glue_create, &hw_glue_callbacks },
364  { "glue-or", hw_glue_create, &hw_glue_callbacks },
365  { "glue-xor", hw_glue_create, &hw_glue_callbacks },
366  { "glue-nor", hw_glue_create, &hw_glue_callbacks },
367  { "glue-not", hw_glue_create, &hw_glue_callbacks },
368  { NULL },
369};
370
371#endif /* _HW_GLUE_C_ */
372