OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [gdb-5.0/] [sim/] [ppc/] [hw_com.c] - Blame information for rev 1782

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 106 markom
/*  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_COM_C_
23
#define _HW_COM_C_
24
 
25
#ifndef STATIC_INLINE_HW_COM
26
#define STATIC_INLINE_HW_COM STATIC_INLINE
27
#endif
28
 
29
#include "device_table.h"
30
 
31
#ifdef HAVE_STRING_H
32
#include <string.h>
33
#else
34
#ifdef HAVE_STRINGS_H
35
#include <strings.h>
36
#endif
37
#endif
38
 
39
#ifdef HAVE_UNISTD_H
40
#include <unistd.h>
41
#endif
42
#ifdef HAVE_STDLIB_H
43
#include <stdlib.h>
44
#endif
45
 
46
/* DEVICE
47
 
48
 
49
   com - '550 compatible serial device
50
 
51
 
52
   DESCRIPTION
53
 
54
 
55
   Models the basics of the 8 register '550 serial device.  The model
56
   includes an interrupt line, input and output fifos, and status
57
   information.
58
 
59
   Independent configuration of the devices input and output streams is
60
   allowed: use either the console or a file (buffered or unbuffered) as
61
   the data source/sink; specify the real-time delay between each character
62
   transfer.
63
 
64
   When the devices input stream is being taken from a file, the end of
65
   file is signaled by a loss of carrier (the loss of carrier may be
66
   incorrectly proceeded by a single null character).
67
 
68
 
69
   PROPERTIES
70
 
71
 
72
   reg = <address> <size> ... (optional - note 1)
73
 
74
   List of <address> <size> pairs.  Each pair specifies an address for
75
   the devices 8 registers.  The address should be 8 byte aligned.
76
 
77
 
78
   alternate-reg = <address> <size> ... (optional - note 1)
79
 
80
   Alternative addreses for the registers.
81
 
82
 
83
   assigned-addresses = <address> <size> ... (optional - note 1)
84
 
85
   On a PCI bus, this property specifies the addresses assigned to the
86
   device.  The values reflect the devices configuration base registers.
87
 
88
   Note 1: At least one of "assigned-addresses", "reg" or "alternative-reg"
89
   must be specified.  If "assigned-addresses" is specified the other
90
   address specifications are ignored.
91
 
92
 
93
   input-file = <file-name> (optional)
94
 
95
   File to take all serial port input from (instead of the simulation
96
   console).
97
 
98
 
99
   output-file = <file-name> (optional)
100
 
101
   File to send all output to (instead of the simulation console).
102
 
103
 
104
   input-buffering = "unbuffered" (optional)
105
 
106
   Specifying "unbuffered" buffering disables buffering on the serial
107
   devices input stream (all data is immediatly read).  In the future,
108
   this option may be used to provide input buffering alternatives.
109
 
110
 
111
   output-buffering = "unbuffered" (optional)
112
 
113
   Specifying "unbuffered" buffering disables buffering on the serial
114
   devices output stream (all data is immediatly written).  In the future,
115
   this option may be extended to include other buffering alternatives.
116
 
117
 
118
   input-delay = <integer-delay> (optional)
119
 
120
   Specify the number of ticks after the current character has been
121
   read from the serial port that the next character becomes
122
   available.
123
 
124
 
125
   output-delay = <integer-delay> (optional)
126
 
127
   Specify the number of ticks after a character has been written to
128
   the empty output fifo that the fifo finishes draining.  Any
129
   characters written to the output fifo before it has drained will
130
   not be lost and will still be displayed.
131
 
132
 
133
   EXAMPLES
134
 
135
 
136
   |  /iobus@0xf0000000/com@0x3000/reg 0x3000 8
137
 
138
   Create a simple console device at address <<0x3000>> within
139
   <<iobus>>.  Since iobus starts at address <<0xf0000000>> the
140
   absolute address of the serial port will be <<0xf0003000>>.
141
 
142
   The device will always be ready for I/O (no delay properties specified)
143
   and both the input and output streams will use the simulation console
144
   (no file properties).
145
 
146
 
147
   |  $ psim \
148
   |    -o '/cpus/cpu@0' \
149
   |    -o '/iobus@0xf0000000/com@0x4000/reg 0x4000 8' \
150
   |    -o '/iobus@0xf0000000/com@0x4000/input-file /etc/passwd' \
151
   |    -o '/iobus@0xf0000000/com@0x4000/input-delay 1000' \
152
   |    -o '/iobus@0xf0000000/com@0x4000 > 0 int /cpus/cpu@0x0' \
153
   |    psim-test/hw-com/cat.be 0xf0004000
154
 
155
   The serial port (at address <<0xf0004000>> is configured so that it
156
   takes its input from the file <</etc/passwd>> while its output is
157
   allowed to appear on the simulation console.
158
 
159
   The node <</cpus/cpu@0>> was explicitly specified to ensure that it had
160
   been created before any interrupts were attached to it.
161
 
162
   The program <<psim-test/hw-com/cat>> copies any characters on the serial
163
   port's input (<</etc/passwd>>) to its output (the console).
164
   Consequently, the aove program will display the contents of the file
165
   <</etc/passwd>> on the screen.
166
 
167
 
168
   BUGS
169
 
170
 
171
   IEEE 1275 requires that a device on a PCI bus have, as its first reg
172
   entry, the address of its configuration space registers.  Currently,
173
   this device does not even implement configuration registers.
174
 
175
   This model does not attempt to model the '550's input and output fifos.
176
   Instead, the input fifo is limited to a single character at a time,
177
   while the output fifo is effectivly infinite.  Consequently, unlike the
178
   '550, this device will not discard output characters once a stream of 16
179
   have been written to the data output register.
180
 
181
   The input and output can only be taken from a file (or the current
182
   terminal device).  In the future, the <<com>> device should allow the
183
   specification of other data streams (such as an xterm or TK window).
184
 
185
   The input blocks if no data is available.
186
 
187
   Interrupts have not been tested.
188
 
189
   */
190
 
191
enum {
192
  max_hw_com_registers = 8,
193
};
194
 
195
typedef struct _com_port {
196
  int ready;
197
  int delay;
198
  int interrupting;
199
  FILE *file;
200
} com_port;
201
 
202
typedef struct _com_modem {
203
  int carrier;
204
  int carrier_changed;
205
  int interrupting;
206
} com_modem;
207
 
208
typedef struct _hw_com_device {
209
  com_port input;
210
  com_port output;
211
  com_modem modem;
212
  char dlab[2];
213
  char reg[max_hw_com_registers];
214
  int interrupting;
215
} hw_com_device;
216
 
217
 
218
static void
219
hw_com_device_init_data(device *me)
220
{
221
  hw_com_device *com = (hw_com_device*)device_data(me);
222
  /* clean up */
223
  if (com->output.file != NULL)
224
    fclose(com->output.file);
225
  if (com->input.file != NULL)
226
    fclose(com->input.file);
227
  memset(com, 0, sizeof(hw_com_device));
228
 
229
  /* the fifo speed */
230
  com->output.delay = (device_find_property(me, "output-delay") != NULL
231
                       ? device_find_integer_property(me, "output-delay")
232
                       : 0);
233
  com->input.delay = (device_find_property(me, "input-delay") != NULL
234
                      ? device_find_integer_property(me, "input-delay")
235
                      : 0);
236
 
237
  /* the data source/sink */
238
  if (device_find_property(me, "input-file") != NULL) {
239
    const char *input_file = device_find_string_property(me, "input-file");
240
    com->input.file = fopen(input_file, "r");
241
    if (com->input.file == NULL)
242
      device_error(me, "Problem opening input file %s\n", input_file);
243
    if (device_find_property(me, "input-buffering") != NULL) {
244
      const char *buffering = device_find_string_property(me, "input-buffering");
245
      if (strcmp(buffering, "unbuffered") == 0)
246
        setbuf(com->input.file, NULL);
247
    }
248
  }
249
  if (device_find_property(me, "output-file") != NULL) {
250
    const char *output_file = device_find_string_property(me, "output-file");
251
    com->output.file = fopen(output_file, "w");
252
    if (com->input.file == NULL)
253
      device_error(me, "Problem opening output file %s\n", output_file);
254
    if (device_find_property(me, "output-buffering") != NULL) {
255
      const char *buffering = device_find_string_property(me, "output-buffering");
256
      if (strcmp(buffering, "unbuffered") == 0)
257
        setbuf(com->output.file, NULL);
258
    }
259
  }
260
 
261
  /* ready from the start */
262
  com->input.ready = 1;
263
  com->modem.carrier = 1;
264
  com->output.ready = 1;
265
}
266
 
267
 
268
static void
269
update_com_interrupts(device *me,
270
                      hw_com_device *com)
271
{
272
  int interrupting;
273
  com->modem.interrupting = (com->modem.carrier_changed && (com->reg[1] & 0x80));
274
  com->input.interrupting = (com->input.ready && (com->reg[1] & 0x1));
275
  com->output.interrupting = (com->output.ready && (com->reg[1] & 0x2));
276
  interrupting = (com->input.interrupting
277
                  || com->output.interrupting
278
                  || com->modem.interrupting);
279
 
280
  if (interrupting) {
281
    if (!com->interrupting) {
282
      device_interrupt_event(me, 0 /*port*/, 1 /*value*/, NULL, 0);
283
    }
284
  }
285
  else /*!interrupting*/ {
286
    if (com->interrupting)
287
      device_interrupt_event(me, 0 /*port*/, 0 /*value*/, NULL, 0);
288
  }
289
  com->interrupting = interrupting;
290
}
291
 
292
 
293
static void
294
make_read_ready(void *data)
295
{
296
  device *me = (device*)data;
297
  hw_com_device *com = (hw_com_device*)device_data(me);
298
  com->input.ready = 1;
299
  update_com_interrupts(me, com);
300
}
301
 
302
static void
303
read_com(device *me,
304
         hw_com_device *com,
305
         unsigned_word a,
306
         char val[1])
307
{
308
  unsigned_word addr = a % 8;
309
 
310
  /* the divisor latch is special */
311
  if (com->reg[3] & 0x8 && addr < 2) {
312
    *val = com->dlab[addr];
313
    return;
314
  }
315
 
316
  switch (addr) {
317
 
318
  case 0:
319
    /* fifo */
320
    if (!com->modem.carrier)
321
      *val = '\0';
322
    if (com->input.ready) {
323
      /* read the char in */
324
      if (com->input.file == NULL) {
325
        if (sim_io_read_stdin(val, 1) < 0)
326
          com->modem.carrier_changed = 1;
327
      }
328
      else {
329
        if (fread(val, 1, 1, com->input.file) == 0)
330
          com->modem.carrier_changed = 1;
331
      }
332
      /* setup for next read */
333
      if (com->modem.carrier_changed) {
334
        /* once lost carrier, never ready */
335
        com->modem.carrier = 0;
336
        com->input.ready = 0;
337
        *val = '\0';
338
      }
339
      else if (com->input.delay > 0) {
340
        com->input.ready = 0;
341
        device_event_queue_schedule(me, com->input.delay, make_read_ready, me);
342
      }
343
    }
344
    else {
345
      /* discard it? */
346
      /* overflow input fifo? */
347
      *val = '\0';
348
    }
349
    break;
350
 
351
  case 2:
352
    /* interrupt ident */
353
    if (com->interrupting) {
354
      if (com->input.interrupting)
355
        *val = 0x4;
356
      else if (com->output.interrupting)
357
        *val = 0x2;
358
      else if (com->modem.interrupting == 0)
359
        *val = 0;
360
      else
361
        device_error(me, "bad elif for interrupts\n");
362
    }
363
    else
364
      *val = 0x1;
365
    break;
366
 
367
  case 5:
368
    /* line status */
369
    *val = ((com->input.ready ? 0x1 : 0)
370
            | (com->output.ready ? 0x60 : 0)
371
            );
372
    break;
373
 
374
  case 6:
375
    /* modem status */
376
    *val = ((com->modem.carrier_changed ? 0x08 : 0)
377
            | (com->modem.carrier ? 0x80 : 0)
378
            );
379
    com->modem.carrier_changed = 0;
380
    break;
381
 
382
  default:
383
    *val = com->reg[addr];
384
    break;
385
 
386
  }
387
  update_com_interrupts(me, com);
388
}
389
 
390
static unsigned
391
hw_com_io_read_buffer_callback(device *me,
392
                               void *dest,
393
                               int space,
394
                               unsigned_word addr,
395
                               unsigned nr_bytes,
396
                               cpu *processor,
397
                               unsigned_word cia)
398
{
399
  hw_com_device *com = device_data(me);
400
  int i;
401
  for (i = 0; i < nr_bytes; i++) {
402
    read_com(me, com, addr + i, &((char*)dest)[i]);
403
  }
404
  return nr_bytes;
405
}
406
 
407
 
408
static void
409
make_write_ready(void *data)
410
{
411
  device *me = (device*)data;
412
  hw_com_device *com = (hw_com_device*)device_data(me);
413
  com->output.ready = 1;
414
  update_com_interrupts(me, com);
415
}
416
 
417
static void
418
write_com(device *me,
419
          hw_com_device *com,
420
          unsigned_word a,
421
          char val)
422
{
423
  unsigned_word addr = a % 8;
424
 
425
  /* the divisor latch is special */
426
  if (com->reg[3] & 0x8 && addr < 2) {
427
    com->dlab[addr] = val;
428
    return;
429
  }
430
 
431
  switch (addr) {
432
 
433
  case 0:
434
    /* fifo */
435
    if (com->output.file == NULL) {
436
      sim_io_write_stdout(&val, 1);
437
    }
438
    else {
439
      fwrite(&val, 1, 1, com->output.file);
440
    }
441
    /* setup for next write */
442
    if (com->output.ready && com->output.delay > 0) {
443
      com->output.ready = 0;
444
      device_event_queue_schedule(me, com->output.delay, make_write_ready, me);
445
    }
446
    break;
447
 
448
  default:
449
    com->reg[addr] = val;
450
    break;
451
 
452
  }
453
  update_com_interrupts(me, com);
454
}
455
 
456
static unsigned
457
hw_com_io_write_buffer_callback(device *me,
458
                                const void *source,
459
                                int space,
460
                                unsigned_word addr,
461
                                unsigned nr_bytes,
462
                                cpu *processor,
463
                                unsigned_word cia)
464
{
465
  hw_com_device *com = device_data(me);
466
  int i;
467
  for (i = 0; i < nr_bytes; i++) {
468
    write_com(me, com, addr + i, ((char*)source)[i]);
469
  }
470
  return nr_bytes;
471
}
472
 
473
 
474
/* instances of the hw_com device */
475
 
476
static void
477
hw_com_instance_delete(device_instance *instance)
478
{
479
  /* nothing to delete, the hw_com is attached to the device */
480
  return;
481
}
482
 
483
static int
484
hw_com_instance_read(device_instance *instance,
485
                     void *buf,
486
                     unsigned_word len)
487
{
488
  device *me = device_instance_device(instance);
489
  hw_com_device *com = device_data(me);
490
  if (com->input.file == NULL)
491
    return sim_io_read_stdin(buf, len);
492
  else {
493
    return fread(buf, 1, len, com->input.file);
494
  }
495
}
496
 
497
static int
498
hw_com_instance_write(device_instance *instance,
499
                      const void *buf,
500
                      unsigned_word len)
501
{
502
  device *me = device_instance_device(instance);
503
  hw_com_device *com = device_data(me);
504
  if (com->output.file == NULL)
505
    return sim_io_write_stdout(buf, len);
506
  else {
507
    return fwrite(buf, 1, len, com->output.file);
508
  }
509
}
510
 
511
static const device_instance_callbacks hw_com_instance_callbacks = {
512
  hw_com_instance_delete,
513
  hw_com_instance_read,
514
  hw_com_instance_write,
515
};
516
 
517
static device_instance *
518
hw_com_create_instance(device *me,
519
                       const char *path,
520
                       const char *args)
521
{
522
  /* point an instance directly at the device */
523
  return device_create_instance_from(me, NULL,
524
                                     device_data(me),
525
                                     path, args,
526
                                     &hw_com_instance_callbacks);
527
}
528
 
529
 
530
static device_callbacks const hw_com_callbacks = {
531
  { generic_device_init_address,
532
    hw_com_device_init_data },
533
  { NULL, }, /* address */
534
  { hw_com_io_read_buffer_callback,
535
      hw_com_io_write_buffer_callback, },
536
  { NULL, }, /* DMA */
537
  { NULL, }, /* interrupt */
538
  { NULL, }, /* unit */
539
  hw_com_create_instance,
540
};
541
 
542
 
543
static void *
544
hw_com_create(const char *name,
545
              const device_unit *unit_address,
546
              const char *args)
547
{
548
  /* create the descriptor */
549
  hw_com_device *hw_com = ZALLOC(hw_com_device);
550
  return hw_com;
551
}
552
 
553
 
554
const device_descriptor hw_com_device_descriptor[] = {
555
  { "com", hw_com_create, &hw_com_callbacks },
556
  { NULL },
557
};
558
 
559
#endif /* _HW_COM_C_ */

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.