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