hw_glue.c revision 1.5
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