hw_glue.c 11 KB

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