New function hw_strdup() - use memory tied to hw device.
[external/binutils.git] / sim / common / dv-glue.c
1 /*  This file is part of the program psim.
2     
3     Copyright (C) 1994-1996,1998 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 #include "sim-main.h"
23 #include "hw-base.h"
24
25 #ifdef HAVE_STRING_H
26 #include <string.h>
27 #else
28 #ifdef HAVE_STRINGS_H
29 #include <strings.h>
30 #endif
31 #endif
32
33 /* DEVICE
34    
35
36    glue - glue to interconnect and test hardware ports
37    
38
39    DESCRIPTION
40    
41
42    The glue device provides two functions.  Firstly, it provides a
43    mechanism for inspecting and driving the port network.  Secondly,
44    it provides a set of boolean primitives that can be used to apply
45    combinatorial operations to the port network.
46
47    Glue devices have a variable number of big endian <<output>>
48    registers.  Each register is target-word sized.  The registers can
49    be read and written.
50
51    Writing to an output register results in an event being driven
52    (level determined by the value written) on the devices
53    corresponding output port.
54
55    Reading an <<output>> register returns either the last value
56    written or the most recently computed value (for that register) as
57    a result of an event ariving on that port (which ever was computed
58    last).
59
60    At present the following sub device types are available:
61
62    <<glue>>: In addition to driving its output interrupt port with any
63    value written to an interrupt input port is stored in the
64    corresponding <<output>> register.  Such input interrupts, however,
65    are not propogated to an output interrupt port.
66
67    <<glue-and>>: The bit-wise AND of the interrupt inputs is computed
68    and then both stored in <<output>> register zero and propogated to
69    output interrupt output port zero.
70
71
72    PROPERTIES
73    
74
75    reg = <address> <size> (required)
76
77    Specify the address (within the parent bus) that this device is to
78    live.  The address must be 2048 * sizeof (word) (8k in a 32bit
79    simulation) aligned.
80
81
82    interrupt-ranges = <int-number> <range> (optional)
83
84    If present, this specifies the number of valid interrupt inputs (up
85    to the maximum of 2048).  By default, <<int-number>> is zero and
86    range is determined by the <<reg>> size.
87
88
89    PORTS
90
91    
92    int[0..] (input, output)
93
94    Both an input and an output port.
95
96
97    EXAMPLES
98
99
100    Enable tracing of the device:
101
102    | -t glue-device \
103
104
105    Create source, bitwize-and, and sink glue devices.  Since the
106    device at address <<0x10000>> is of size <<8>> it will have two
107    output interrupt ports.
108
109    | -o '/iobus@0xf0000000/glue@0x10000/reg 0x10000 8' \
110    | -o '/iobus@0xf0000000/glue-and@0x20000/reg 0x20000 4' \
111    | -o '/iobus@0xf0000000/glue-and/interrupt-ranges 0 2' \
112    | -o '/iobus@0xf0000000/glue@0x30000/reg 0x30000 4' \
113
114
115    Wire the two source interrupts to the AND device:
116
117    | -o '/iobus@0xf0000000/glue@0x10000 > 0 0 /iobus/glue-and' \
118    | -o '/iobus@0xf0000000/glue@0x10000 > 1 1 /iobus/glue-and' \
119
120
121    Wire the AND device up to the sink so that the and's output is not
122    left open.
123
124    | -o '/iobus@0xf0000000/glue-and > 0 0 /iobus/glue@0x30000' \
125
126
127    With the above configuration.  The client program is able to
128    compute a two bit AND.  For instance the <<C>> stub below prints 1
129    AND 0.
130
131    |  unsigned *input = (void*)0xf0010000;
132    |  unsigned *output = (void*)0xf0030000;
133    |  unsigned ans;
134    |  input[0] = htonl(1);
135    |  input[1] = htonl(0);
136    |  ans = ntohl(*output);
137    |  write_string("AND is ");
138    |  write_int(ans);
139    |  write_line();
140    
141
142    BUGS
143
144    
145    A future implementation of this device may support multiple
146    interrupt ranges.
147
148    Some of the devices listed may not yet be fully implemented.
149
150    Additional devices such as a D flip-flop (DFF), an inverter (INV)
151    or a latch (LAT) may prove useful.
152
153    */
154
155
156 enum {
157   max_nr_ports = 2048,
158 };
159
160 enum hw_glue_type {
161   glue_undefined = 0,
162   glue_io,
163   glue_and,
164   glue_nand,
165   glue_or,
166   glue_xor,
167   glue_nor,
168   glue_not,
169 };
170
171 struct hw_glue {
172   enum hw_glue_type type;
173   int int_number;
174   int *input;
175   int nr_inputs;
176   unsigned sizeof_input;
177   /* our output registers */
178   int space;
179   unsigned_word address;
180   unsigned sizeof_output;
181   int *output;
182   int nr_outputs;
183 };
184
185
186 static hw_io_read_buffer_callback hw_glue_io_read_buffer;
187 static hw_io_write_buffer_callback hw_glue_io_write_buffer;
188 static hw_port_event_callback hw_glue_port_event;
189 const static struct hw_port_descriptor hw_glue_ports[];
190
191 static void
192 hw_glue_finish (struct hw *me)
193 {
194   struct hw_glue *glue = HW_ZALLOC (me, struct hw_glue);
195   
196   /* establish our own methods */
197   set_hw_data (me, glue);
198   set_hw_io_read_buffer (me, hw_glue_io_read_buffer);
199   set_hw_io_write_buffer (me, hw_glue_io_write_buffer);
200   set_hw_ports (me, hw_glue_ports);
201   set_hw_port_event (me, hw_glue_port_event);
202
203   /* attach to our parent bus */
204   do_hw_attach_regs (me);
205   
206   /* establish the output registers */
207   {
208     reg_property_spec unit;
209     int reg_nr;
210     /* find a relevant reg entry */
211     reg_nr = 0;
212     while (hw_find_reg_array_property (me, "reg", reg_nr, &unit)
213            && !hw_unit_size_to_attach_size (hw_parent (me),
214                                             &unit.size,
215                                             &glue->sizeof_output,
216                                             me))
217       reg_nr++;
218     /* check out the size */
219     if (glue->sizeof_output == 0)
220       hw_abort (me, "at least one reg property size must be nonzero");
221     if (glue->sizeof_output % sizeof (unsigned_word) != 0)
222       hw_abort (me, "reg property size must be %d aligned",
223                 sizeof (unsigned_word));
224     /* and the address */
225     hw_unit_address_to_attach_address (hw_parent (me),
226                                        &unit.address,
227                                        &glue->space,
228                                        &glue->address,
229                                        me);
230     if (glue->address % (sizeof (unsigned_word) * max_nr_ports) != 0)
231       hw_abort (me, "reg property address must be %d aligned",
232                 sizeof (unsigned_word) * max_nr_ports);
233     glue->nr_outputs = glue->sizeof_output / sizeof (unsigned_word);
234     glue->output = hw_zalloc (me, glue->sizeof_output);
235   }
236   
237   /* establish the input ports */
238   {
239     const struct hw_property *ranges;
240     ranges = hw_find_property (me, "interrupt-ranges");
241     if (ranges == NULL)
242       {
243         glue->int_number = 0;
244         glue->nr_inputs = glue->nr_outputs;
245       }
246     else if (ranges->sizeof_array != sizeof (unsigned_cell) * 2)
247       {
248         hw_abort (me, "invalid interrupt-ranges property (incorrect size)");
249       }
250     else
251       {
252         const unsigned_cell *int_range = ranges->array;
253         glue->int_number = BE2H_cell (int_range[0]);
254         glue->nr_inputs = BE2H_cell (int_range[1]);
255       }
256     glue->sizeof_input = glue->nr_inputs * sizeof (unsigned);
257     glue->input = hw_zalloc (me, glue->sizeof_input);
258   }
259   
260   /* determine our type */
261   {
262     const char *name = hw_name(me);
263     if (strcmp (name, "glue") == 0)
264       glue->type = glue_io;
265     else if (strcmp (name, "glue-and") == 0)
266       glue->type = glue_and;
267     else
268       hw_abort (me, "unimplemented glue type");
269   }
270   
271   HW_TRACE ((me, "int-number %d, nr_inputs %d, nr_outputs %d",
272              glue->int_number, glue->nr_inputs, glue->nr_outputs));
273 }
274
275 static unsigned
276 hw_glue_io_read_buffer (struct hw *me,
277                         void *dest,
278                         int space,
279                         unsigned_word addr,
280                         unsigned nr_bytes,
281                         sim_cpu *cpu,
282                         sim_cia cia)
283 {
284   struct hw_glue *glue = (struct hw_glue *) hw_data (me);
285   int reg = ((addr - glue->address) / sizeof (unsigned_word)) % glue->nr_outputs;
286   if (nr_bytes != sizeof (unsigned_word)
287       || (addr % sizeof (unsigned_word)) != 0)
288     hw_abort (me, "missaligned read access (%d:0x%lx:%d) not supported",
289               space, (unsigned long)addr, nr_bytes);
290   *(unsigned_word*)dest = H2BE_4(glue->output[reg]);
291   HW_TRACE ((me, "read - port %d (0x%lx), level %d",
292              reg, (unsigned long) addr, glue->output[reg]));
293   return nr_bytes;
294 }
295
296
297 static unsigned
298 hw_glue_io_write_buffer (struct hw *me,
299                          const void *source,
300                          int space,
301                          unsigned_word addr,
302                          unsigned nr_bytes,
303                          sim_cpu *cpu,
304                          sim_cia cia)
305 {
306   struct hw_glue *glue = (struct hw_glue *) hw_data (me);
307   int reg = ((addr - glue->address) / sizeof (unsigned_word)) % max_nr_ports;
308   if (nr_bytes != sizeof (unsigned_word)
309       || (addr % sizeof (unsigned_word)) != 0)
310     hw_abort (me, "missaligned write access (%d:0x%lx:%d) not supported",
311               space, (unsigned long) addr, nr_bytes);
312   glue->output[reg] = H2BE_4 (*(unsigned_word*)source);
313   HW_TRACE ((me, "write - port %d (0x%lx), level %d",
314              reg, (unsigned long) addr, glue->output[reg]));
315   hw_port_event (me, reg, glue->output[reg], cpu, cia);
316   return nr_bytes;
317 }
318
319 static void
320 hw_glue_port_event (struct hw *me,
321                     int my_port,
322                     struct hw *source,
323                     int source_port,
324                     int level,
325                     sim_cpu *cpu,
326                     sim_cia cia)
327 {
328   struct hw_glue *glue = (struct hw_glue *) hw_data (me);
329   int i;
330   if (my_port < glue->int_number
331       || my_port >= glue->int_number + glue->nr_inputs)
332     hw_abort (me, "port %d outside of valid range", my_port);
333   glue->input[my_port - glue->int_number] = level;
334   switch (glue->type)
335     {
336     case glue_io:
337       {
338         int port = my_port % glue->nr_outputs;
339         glue->output[port] = level;
340         HW_TRACE ((me, "input - port %d (0x%lx), level %d",
341                    my_port,
342                    (unsigned long) glue->address + port * sizeof (unsigned_word),
343                    level));
344         break;
345       }
346     case glue_and:
347       {
348         glue->output[0] = glue->input[0];
349         for (i = 1; i < glue->nr_inputs; i++)
350           glue->output[0] &= glue->input[i];
351         HW_TRACE ((me, "and - port %d, level %d arrived - output %d",
352                    my_port, level, glue->output[0]));
353         hw_port_event (me, 0, glue->output[0], cpu, cia);
354         break;
355       }
356     default:
357       {
358         hw_abort (me, "operator not implemented");
359         break;
360       }
361     }
362 }
363
364
365 static const struct hw_port_descriptor hw_glue_ports[] = {
366   { "int", 0, max_nr_ports },
367   { NULL }
368 };
369
370
371 const struct hw_device_descriptor dv_glue_descriptor[] = {
372   { "glue", hw_glue_finish, },
373   { "glue-and", hw_glue_finish, },
374   { "glue-nand", hw_glue_finish, },
375   { "glue-or", hw_glue_finish, },
376   { "glue-xor", hw_glue_finish, },
377   { "glue-nor", hw_glue_finish, },
378   { "glue-not", hw_glue_finish, },
379   { NULL },
380 };