1 /* The common simulator framework for GDB, the GNU Debugger.
2 
3    Copyright 2002, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4 
5    Contributed by Andrew Cagney and Red Hat.
6 
7    This file is part of GDB.
8 
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 3 of the License, or
12    (at your option) any later version.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
21 
22 
23 #include "hw-main.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 {
158   max_nr_ports = 2048,
159 };
160 
161 enum hw_glue_type
162 {
163   glue_undefined = 0,
164   glue_io,
165   glue_and,
166   glue_nand,
167   glue_or,
168   glue_xor,
169   glue_nor,
170   glue_not,
171 };
172 
173 struct hw_glue
174 {
175   enum hw_glue_type type;
176   int int_number;
177   int *input;
178   int nr_inputs;
179   unsigned sizeof_input;
180   /* our output registers */
181   int space;
182   unsigned_word address;
183   unsigned sizeof_output;
184   int *output;
185   int nr_outputs;
186 };
187 
188 
189 static hw_io_read_buffer_method hw_glue_io_read_buffer;
190 static hw_io_write_buffer_method hw_glue_io_write_buffer;
191 static hw_port_event_method hw_glue_port_event;
192 static const struct hw_port_descriptor hw_glue_ports[];
193 
194 static void
hw_glue_finish(struct hw * me)195 hw_glue_finish (struct hw *me)
196 {
197   struct hw_glue *glue = HW_ZALLOC (me, struct hw_glue);
198 
199   /* establish our own methods */
200   set_hw_data (me, glue);
201   set_hw_io_read_buffer (me, hw_glue_io_read_buffer);
202   set_hw_io_write_buffer (me, hw_glue_io_write_buffer);
203   set_hw_ports (me, hw_glue_ports);
204   set_hw_port_event (me, hw_glue_port_event);
205 
206   /* attach to our parent bus */
207   do_hw_attach_regs (me);
208 
209   /* establish the output registers */
210   {
211     reg_property_spec unit;
212     int reg_nr;
213 
214     /* find a relevant reg entry */
215     reg_nr = 0;
216     while (hw_find_reg_array_property (me, "reg", reg_nr, &unit)
217 	   && !hw_unit_size_to_attach_size (hw_parent (me),
218 					    &unit.size,
219 					    &glue->sizeof_output,
220 					    me))
221       reg_nr++;
222 
223     /* check out the size */
224     if (glue->sizeof_output == 0)
225       hw_abort (me, "at least one reg property size must be nonzero");
226     if (glue->sizeof_output % sizeof (unsigned_word) != 0)
227       hw_abort (me, "reg property size must be %ld aligned",
228 		(long) sizeof (unsigned_word));
229 
230     /* and the address */
231     hw_unit_address_to_attach_address (hw_parent (me),
232 				       &unit.address,
233 				       &glue->space,
234 				       &glue->address,
235 				       me);
236     if (glue->address % (sizeof (unsigned_word) * max_nr_ports) != 0)
237       hw_abort (me, "reg property address must be %ld aligned",
238 		(long) (sizeof (unsigned_word) * max_nr_ports));
239 
240     glue->nr_outputs = glue->sizeof_output / sizeof (unsigned_word);
241     glue->output = hw_zalloc (me, glue->sizeof_output);
242   }
243 
244   /* establish the input ports */
245   {
246     const struct hw_property *ranges;
247 
248     ranges = hw_find_property (me, "interrupt-ranges");
249     if (ranges == NULL)
250       {
251 	glue->int_number = 0;
252 	glue->nr_inputs = glue->nr_outputs;
253       }
254     else if (ranges->sizeof_array != sizeof (unsigned_cell) * 2)
255       {
256 	hw_abort (me, "invalid interrupt-ranges property (incorrect size)");
257       }
258     else
259       {
260 	const unsigned_cell *int_range = ranges->array;
261 
262 	glue->int_number = BE2H_cell (int_range[0]);
263 	glue->nr_inputs = BE2H_cell (int_range[1]);
264       }
265     glue->sizeof_input = glue->nr_inputs * sizeof (unsigned);
266     glue->input = hw_zalloc (me, glue->sizeof_input);
267   }
268 
269   /* determine our type */
270   {
271     const char *name = hw_name(me);
272 
273     if (strcmp (name, "glue") == 0)
274       glue->type = glue_io;
275     else if (strcmp (name, "glue-and") == 0)
276       glue->type = glue_and;
277     else
278       hw_abort (me, "unimplemented glue type");
279   }
280 
281   HW_TRACE ((me, "int-number %d, nr_inputs %d, nr_outputs %d",
282 	     glue->int_number, glue->nr_inputs, glue->nr_outputs));
283 }
284 
285 static unsigned
hw_glue_io_read_buffer(struct hw * me,void * dest,int space,unsigned_word addr,unsigned nr_bytes)286 hw_glue_io_read_buffer (struct hw *me,
287 			void *dest,
288 			int space,
289 			unsigned_word addr,
290 			unsigned nr_bytes)
291 {
292   struct hw_glue *glue = (struct hw_glue *) hw_data (me);
293   int reg = ((addr - glue->address) / sizeof (unsigned_word)) % glue->nr_outputs;
294 
295   if (nr_bytes != sizeof (unsigned_word)
296       || (addr % sizeof (unsigned_word)) != 0)
297     hw_abort (me, "missaligned read access (%d:0x%lx:%d) not supported",
298 	      space, (unsigned long)addr, nr_bytes);
299 
300   *(unsigned_word *)dest = H2BE_4 (glue->output[reg]);
301 
302   HW_TRACE ((me, "read - port %d (0x%lx), level %d",
303 	     reg, (unsigned long) addr, glue->output[reg]));
304 
305   return nr_bytes;
306 }
307 
308 
309 static unsigned
hw_glue_io_write_buffer(struct hw * me,const void * source,int space,unsigned_word addr,unsigned nr_bytes)310 hw_glue_io_write_buffer (struct hw *me,
311 			 const void *source,
312 			 int space,
313 			 unsigned_word addr,
314 			 unsigned nr_bytes)
315 {
316   struct hw_glue *glue = (struct hw_glue *) hw_data (me);
317   int reg = ((addr - glue->address) / sizeof (unsigned_word)) % max_nr_ports;
318 
319   if (nr_bytes != sizeof (unsigned_word)
320       || (addr % sizeof (unsigned_word)) != 0)
321     hw_abort (me, "missaligned write access (%d:0x%lx:%d) not supported",
322 	      space, (unsigned long) addr, nr_bytes);
323 
324   glue->output[reg] = H2BE_4 (*(unsigned_word *)source);
325 
326   HW_TRACE ((me, "write - port %d (0x%lx), level %d",
327 	     reg, (unsigned long) addr, glue->output[reg]));
328 
329   hw_port_event (me, reg, glue->output[reg]);
330 
331   return nr_bytes;
332 }
333 
334 static void
hw_glue_port_event(struct hw * me,int my_port,struct hw * source,int source_port,int level)335 hw_glue_port_event (struct hw *me,
336 		    int my_port,
337 		    struct hw *source,
338 		    int source_port,
339 		    int level)
340 {
341   struct hw_glue *glue = (struct hw_glue *) hw_data (me);
342   int i;
343 
344   if (my_port < glue->int_number
345       || my_port >= glue->int_number + glue->nr_inputs)
346     hw_abort (me, "port %d outside of valid range", my_port);
347 
348   glue->input[my_port - glue->int_number] = level;
349   switch (glue->type)
350     {
351     case glue_io:
352       {
353 	int port = my_port % glue->nr_outputs;
354 
355 	glue->output[port] = level;
356 
357 	HW_TRACE ((me, "input - port %d (0x%lx), level %d",
358 		   my_port,
359 		   (unsigned long) glue->address + port * sizeof (unsigned_word),
360 		   level));
361 	break;
362       }
363     case glue_and:
364       {
365 	glue->output[0] = glue->input[0];
366 	for (i = 1; i < glue->nr_inputs; i++)
367 	  glue->output[0] &= glue->input[i];
368 
369 	HW_TRACE ((me, "and - port %d, level %d arrived - output %d",
370 		   my_port, level, glue->output[0]));
371 
372 	hw_port_event (me, 0, glue->output[0]);
373 	break;
374       }
375     default:
376       {
377 	hw_abort (me, "operator not implemented");
378 	break;
379       }
380     }
381 }
382 
383 
384 static const struct hw_port_descriptor hw_glue_ports[] =
385 {
386   { "int", 0, max_nr_ports, 0 },
387   { NULL, 0, 0, 0 }
388 };
389 
390 
391 const struct hw_descriptor dv_glue_descriptor[] =
392 {
393   { "glue", hw_glue_finish, },
394   { "glue-and", hw_glue_finish, },
395   { "glue-nand", hw_glue_finish, },
396   { "glue-or", hw_glue_finish, },
397   { "glue-xor", hw_glue_finish, },
398   { "glue-nor", hw_glue_finish, },
399   { "glue-not", hw_glue_finish, },
400   { NULL, NULL },
401 };
402