hw_glue.c revision 1.3
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 3 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, see <http://www.gnu.org/licenses/>.
17
18    */
19
20
21#ifndef _HW_GLUE_C_
22#define _HW_GLUE_C_
23
24#include "device_table.h"
25
26
27/* DEVICE
28
29
30   glue - glue to interconnect and test interrupts
31
32
33   DESCRIPTION
34
35
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.
40
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.
44
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.
48
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).
52
53   At present the following sub device types are available:
54
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.
59
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.
63
64
65   PROPERTIES
66
67
68   reg = <address> <size> (required)
69
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.
73
74
75   interrupt-ranges = <int-number> <range> (optional)
76
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.
80
81
82   EXAMPLES
83
84
85   Enable tracing of the device:
86
87   | -t glue-device \
88
89
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.
93
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' \
98
99
100   Wire the two source interrupts to the AND device:
101
102   | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \
103   | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \
104
105
106   Wire the AND device up to the sink so that the and's output is not
107   left open.
108
109   | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \
110
111
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.
115
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();
125
126
127   BUGS
128
129
130   A future implementation of this device may support multiple
131   interrupt ranges.
132
133   Some of the devices listed may not yet be fully implemented.
134
135   Additional devices such as a dff, an inverter or a latch may be
136   useful.
137
138   */
139
140
141enum {
142  max_nr_interrupts = 2048,
143};
144
145typedef 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;
155
156typedef 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;
169
170
171static void
172hw_glue_init_address(device *me)
173{
174  hw_glue_device *glue = (hw_glue_device*)device_data(me);
175
176  /* attach to my parent */
177  generic_device_init_address(me);
178
179  /* establish the output registers */
180  if (glue->output != NULL) {
181    memset(glue->output, 0, glue->sizeof_output);
182  }
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 %d 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 %d 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);
206  }
207
208  /* establish the input interrupt ports */
209  if (glue->input != NULL) {
210    memset(glue->input, 0, glue->sizeof_input);
211  }
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;
217    }
218    else if (ranges->sizeof_array != sizeof(unsigned_cell) * 2) {
219      device_error(me, "invalid interrupt-ranges property (incorrect size)");
220    }
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]);
225    }
226    glue->sizeof_input = glue->nr_inputs * sizeof(unsigned);
227    glue->input = zalloc(glue->sizeof_input);
228  }
229
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");
239  }
240
241  DTRACE(glue, ("int-number %d, nr_inputs %d, nr_outputs %d\n",
242		glue->int_number, glue->nr_inputs, glue->nr_outputs));
243}
244
245static unsigned
246hw_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)
253{
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;
264}
265
266
267static unsigned
268hw_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)
275{
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;
287}
288
289static void
290hw_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)
297{
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:
306    {
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;
314    }
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;
326  }
327}
328
329
330static const device_interrupt_port_descriptor hw_glue_interrupt_ports[] = {
331  { "int", 0, max_nr_interrupts },
332  { NULL }
333};
334
335
336static 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 */
345};
346
347
348static void *
349hw_glue_create(const char *name,
350	      const device_unit *unit_address,
351	      const char *args)
352{
353  /* create the descriptor */
354  hw_glue_device *glue = ZALLOC(hw_glue_device);
355  return glue;
356}
357
358
359const 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 },
368};
369
370#endif /* _HW_GLUE_C_ */
371