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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [or1ksim/] [vapi/] [vapi.c] - Blame information for rev 658

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 19 jeremybenn
/* vapi.c -- Verification API Interface
2
 
3
   Copyright (C) 2001, Marko Mlinar, markom@opencores.org
4
   Copyright (C) 2008 Embecosm Limited
5
 
6
   Contributor Jeremy Bennett <jeremy.bennett@embecosm.com>
7
 
8
   This file is part of OpenRISC 1000 Architectural Simulator.
9
 
10
   This program is free software; you can redistribute it and/or modify it
11
   under the terms of the GNU General Public License as published by the Free
12
   Software Foundation; either version 3 of the License, or (at your option)
13
   any later version.
14
 
15
   This program is distributed in the hope that it will be useful, but WITHOUT
16
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
18
   more details.
19
 
20
   You should have received a copy of the GNU General Public License along
21
   with this program.  If not, see <http://www.gnu.org/licenses/>. */
22
 
23
/* This program is commented throughout in a fashion suitable for processing
24
   with Doxygen. */
25
 
26
 
27
/* Autoconf and/or portability configuration */
28
#include "config.h"
29
#include "port.h"
30
 
31
/* System includes */
32
#include <stdlib.h>
33
#include <unistd.h>
34
#include <stdio.h>
35
#include <errno.h>
36
#include <sys/poll.h>
37
#include <sys/socket.h>
38
#include <netinet/in.h>
39
#include <fcntl.h>
40
#include <netdb.h>
41
#include <netinet/tcp.h>
42
 
43
/* Package includes */
44
#include "sim-config.h"
45
#include "vapi.h"
46
 
47
 
48
static unsigned int serverIP = 0;
49
 
50
static unsigned int server_fd = 0;
51
static unsigned int nhandlers = 0;
52
 
53
static int tcp_level = 0;
54
 
55
static struct vapi_handler
56
{
57
  int fd;
58
  unsigned long base_id, num_ids;
59
  void (*read_func) (unsigned long, unsigned long, void *);
60
  void *priv_dat;
61
  struct vapi_handler *next;
62
  int temp;
63
} *vapi_handler = NULL;
64
 
65
/* Structure for polling, it is cached, that it doesn't have to be rebuilt each time */
66
static struct pollfd *fds = NULL;
67
static int nfds = 0;
68
 
69
/* Rebuilds the fds structures; see fds.  */
70
void
71
rebuild_fds ()
72
{
73
  struct vapi_handler *t;
74
  if (fds)
75
    free (fds);
76
  fds = (struct pollfd *) malloc (sizeof (struct pollfd) * (nhandlers + 1));
77
  if (!fds)
78
    {
79
      fprintf (stderr, "FATAL: Out of memory.\n");
80
      exit (1);
81
    }
82
 
83
  nfds = 0;
84
  fds[nfds].fd = server_fd;
85
  fds[nfds].events = POLLIN;
86
  fds[nfds++].revents = 0;
87
 
88
  for (t = vapi_handler; t; t = t->next)
89
    {
90
      if (t->fd)
91
        {
92
          t->temp = nfds;
93
          fds[nfds].fd = t->fd;
94
          fds[nfds].events = POLLIN;
95
          fds[nfds++].revents = 0;
96
        }
97
      else
98
        t->temp = -1;
99
    }
100
}
101
 
102
/* Determines whether a certain handler handles an ID */
103
static int
104
handler_fits_id (const struct vapi_handler *t, unsigned long id)
105
{
106
  return ((id >= t->base_id) && (id < t->base_id + t->num_ids));
107
}
108
 
109
/* Finds a handler with given ID, return it, NULL if not found.  */
110
static struct vapi_handler *
111
find_handler (unsigned long id)
112
{
113
  struct vapi_handler *t = vapi_handler;
114
  while (t && !handler_fits_id (t, id))
115
    t = t->next;
116
  return t;
117
}
118
 
119
/* Adds a handler with given id and returns it.  */
120
static struct vapi_handler *
121
add_handler (unsigned long base_id, unsigned long num_ids)
122
{
123
  struct vapi_handler **t = &vapi_handler;
124
  struct vapi_handler *tt;
125
  while ((*t))
126
    t = &(*t)->next;
127
  tt = (struct vapi_handler *) malloc (sizeof (struct vapi_handler));
128
  tt->next = NULL;
129
  tt->base_id = base_id;
130
  tt->num_ids = num_ids;
131
  tt->read_func = NULL;
132
  tt->priv_dat = NULL;
133
  tt->fd = 0;
134
  (*t) = tt;
135
  free (fds);
136
  fds = NULL;
137
  nhandlers++;
138
  rebuild_fds ();
139
  return tt;
140
}
141
 
142
void
143
vapi_write_log_file (VAPI_COMMAND command, unsigned long devid,
144
                     unsigned long data)
145
{
146
  if (!runtime.vapi.vapi_file)
147
    return;
148
  if (!config.vapi.hide_device_id && devid <= VAPI_MAX_DEVID)
149
    fprintf (runtime.vapi.vapi_file, "%04lx", devid);
150
  fprintf (runtime.vapi.vapi_file, "%1x%08lx\n", command, data);
151
}
152
 
153
static int
154
vapi_write_stream (int fd, void *buf, int len)
155
{
156
  int n;
157
  char *w_buf = (char *) buf;
158
  struct pollfd block;
159
 
160
  while (len)
161
    {
162
      if ((n = write (fd, w_buf, len)) < 0)
163
        {
164
          switch (errno)
165
            {
166
            case EWOULDBLOCK:   /* or EAGAIN */
167
              /* We've been called on a descriptor marked
168
                 for nonblocking I/O. We better simulate
169
                 blocking behavior. */
170
              block.fd = fd;
171
              block.events = POLLOUT;
172
              block.revents = 0;
173
              poll (&block, 1, -1);
174
              continue;
175
            case EINTR:
176
              continue;
177
            case EPIPE:
178
              close (fd);
179
              fd = 0;
180
              return -1;
181
            default:
182
              return -1;
183
            }
184
        }
185
      else
186
        {
187
          len -= n;
188
          w_buf += n;
189
        }
190
    }
191
  return 0;
192
}
193
 
194
static int
195
vapi_read_stream (int fd, void *buf, int len)
196
{
197
  int n;
198
  char *r_buf = (char *) buf;
199
  struct pollfd block;
200
 
201
  while (len)
202
    {
203
      if ((n = read (fd, r_buf, len)) < 0)
204
        {
205
          switch (errno)
206
            {
207
            case EWOULDBLOCK:   /* or EAGAIN */
208
              /* We've been called on a descriptor marked
209
                 for nonblocking I/O. We better simulate
210
                 blocking behavior. */
211
              block.fd = fd;
212
              block.events = POLLIN;
213
              block.revents = 0;
214
              poll (&block, 1, -1);
215
              continue;
216
            case EINTR:
217
              continue;
218
            default:
219
              return -1;
220
            }
221
        }
222
      else if (n == 0)
223
        {
224
          close (fd);
225
          fd = 0;
226
          return -1;
227
        }
228
      else
229
        {
230
          len -= n;
231
          r_buf += n;
232
        }
233
    }
234
  return 0;
235
}
236
 
237
/* Added by CZ 24/05/01 */
238
int
239
get_server_socket (const char *name, const char *proto, int port)
240
{
241
  struct servent *service;
242
  struct protoent *protocol;
243
  struct sockaddr_in sa;
244
  struct hostent *hp;
245
  int sockfd;
246
  socklen_t len;
247
  char myname[256];
248
  int flags;
249
  char sTemp[256];
250
 
251
  /* First, get the protocol number of TCP */
252
  if (!(protocol = getprotobyname (proto)))
253
    {
254
      sprintf (sTemp, "Unable to load protocol \"%s\"", proto);
255
      perror (sTemp);
256
      return 0;
257
    }
258
  tcp_level = protocol->p_proto;        /* Save for later */
259
 
260
  /* If we weren't passed a non standard port, get the port
261
     from the services directory. */
262
  if (!port)
263
    {
264
      if ((service = getservbyname (name, protocol->p_name)))
265
        port = ntohs (service->s_port);
266
    }
267
 
268
  /* Create the socket using the TCP protocol */
269
  if ((sockfd = socket (PF_INET, SOCK_STREAM, protocol->p_proto)) < 0)
270
    {
271
      perror ("Unable to create socket");
272
      return 0;
273
    }
274
 
275
  flags = 1;
276
  if (setsockopt
277
      (sockfd, SOL_SOCKET, SO_REUSEADDR, (const char *) &flags,
278
       sizeof (int)) < 0)
279
    {
280
      sprintf (sTemp, "Can not set SO_REUSEADDR option on socket %d", sockfd);
281
      perror (sTemp);
282
      close (sockfd);
283
      return 0;
284
    }
285
 
286
  /* The server should also be non blocking. Get the current flags. */
287
  if (fcntl (sockfd, F_GETFL, &flags) < 0)
288
    {
289
      sprintf (sTemp, "Unable to get flags for socket %d", sockfd);
290
      perror (sTemp);
291
      close (sockfd);
292
      return 0;
293
    }
294
 
295
  /* Set the nonblocking flag */
296
  if (fcntl (sockfd, F_SETFL, flags | O_NONBLOCK) < 0)
297
    {
298
      sprintf (sTemp, "Unable to set flags for socket %d to value 0x%08x",
299
               sockfd, flags | O_NONBLOCK);
300
      perror (sTemp);
301
      close (sockfd);
302
      return 0;
303
    }
304
 
305
  /* Find out what our address is */
306
  memset (&sa, 0, sizeof (struct sockaddr_in));
307
  gethostname (myname, sizeof (myname));
308
  if (!(hp = gethostbyname (myname)))
309
    {
310
      perror ("Unable to read hostname");
311
      close (sockfd);
312
      return 0;
313
    }
314
 
315
  /* Bind our socket to the appropriate address */
316
  sa.sin_family = hp->h_addrtype;
317
  sa.sin_port = htons (port);
318
  if (bind (sockfd, (struct sockaddr *) &sa, sizeof (struct sockaddr_in)) < 0)
319
    {
320
      sprintf (sTemp, "Unable to bind socket %d to port %d", sockfd, port);
321
      perror (sTemp);
322
      close (sockfd);
323
      return 0;
324
    }
325
  serverIP = sa.sin_addr.s_addr;
326
  len = sizeof (struct sockaddr_in);
327
  if (getsockname (sockfd, (struct sockaddr *) &sa, &len) < 0)
328
    {
329
      sprintf (sTemp, "Unable to get socket information for socket %d",
330
               sockfd);
331
      perror (sTemp);
332
      close (sockfd);
333
      return 0;
334
    }
335
  runtime.vapi.server_port = ntohs (sa.sin_port);
336
 
337
  /* Set the backlog to 1 connections */
338
  if (listen (sockfd, 1) < 0)
339
    {
340
      sprintf (sTemp, "Unable to set backlog on socket %d to %d", sockfd, 1);
341
      perror (sTemp);
342
      close (sockfd);
343
      return 0;
344
    }
345
 
346
  return sockfd;
347
}
348
 
349
static void
350
server_request ()
351
{
352
  struct sockaddr_in sa;
353
  struct sockaddr *addr = (struct sockaddr *) &sa;
354
  socklen_t len = sizeof (struct sockaddr_in);
355
  int fd = accept (server_fd, addr, &len);
356
  int on_off = 0;                /* Turn off Nagel's algorithm on the socket */
357
  int flags;
358
  char sTemp[256];
359
 
360
  if (fd < 0)
361
    {
362
      /* This is valid, because a connection could have started,
363
         and then terminated due to a protocol error or user
364
         initiation before the accept could take place. */
365
      if (errno != EWOULDBLOCK && errno != EAGAIN)
366
        {
367
          perror ("accept");
368
          close (server_fd);
369
          server_fd = 0;
370
          runtime.vapi.enabled = 0;
371
          serverIP = 0;
372
        }
373
      return;
374
    }
375
 
376
  if (fcntl (fd, F_GETFL, &flags) < 0)
377
    {
378
      sprintf (sTemp, "Unable to get flags for vapi socket %d", fd);
379
      perror (sTemp);
380
      close (fd);
381
      return;
382
    }
383
 
384
  if (fcntl (fd, F_SETFL, flags | O_NONBLOCK) < 0)
385
    {
386
      sprintf (sTemp,
387
               "Unable to set flags for vapi socket %d to value 0x%08x", fd,
388
               flags | O_NONBLOCK);
389
      perror (sTemp);
390
      close (fd);
391
      return;
392
    }
393
 
394
  if (setsockopt (fd, tcp_level, TCP_NODELAY, &on_off, sizeof (int)) < 0)
395
    {
396
      sprintf (sTemp,
397
               "Unable to disable Nagel's algorithm for socket %d.\nsetsockopt",
398
               fd);
399
      perror (sTemp);
400
      close (fd);
401
      return;
402
    }
403
 
404
  /* Install new handler */
405
  {
406
    unsigned long id;
407
    struct vapi_handler *t;
408
    if (vapi_read_stream (fd, &id, sizeof (id)))
409
      {
410
        perror ("Cannot get id");
411
        close (fd);
412
        return;
413
      }
414
    t = find_handler (id);
415
    if (t)
416
      {
417
        if (t->fd)
418
          {
419
            fprintf (stderr,
420
                     "WARNING: Test with id %lx already connected. Ignoring.\n",
421
                     id);
422
            close (fd);
423
            return;
424
          }
425
        else
426
          {
427
            t->fd = fd;
428
            rebuild_fds ();
429
          }
430
      }
431
    else
432
      {
433
        fprintf (stderr,
434
                 "WARNING: Test with id %lx not registered. Ignoring.\n", id);
435
        close (fd);             /* kill the connection */
436
        return;
437
      }
438
    if (config.sim.verbose)
439
      PRINTF ("\nConnection with test (id %lx) established.\n", id);
440
  }
441
}
442
 
443
static int
444
write_packet (unsigned long id, unsigned long data)
445
{
446
  struct vapi_handler *t = find_handler (id);
447
  if (!t || !t->fd)
448
    return 1;
449
  id = htonl (id);
450
  if (vapi_write_stream (t->fd, &id, sizeof (id)) < 0)
451
    return 1;
452
  data = htonl (data);
453
  if (vapi_write_stream (t->fd, &data, sizeof (data)) < 0)
454
    return 1;
455
  return 0;
456
}
457
 
458
static int
459
read_packet (int fd, unsigned long *id, unsigned long *data)
460
{
461
  if (fd <= 0)
462
    return 1;
463
  if (vapi_read_stream (fd, id, sizeof (unsigned long)) < 0)
464
    return 1;
465
  *id = ntohl (*id);
466
  if (vapi_read_stream (fd, data, sizeof (unsigned long)) < 0)
467
    return 1;
468
  *data = ntohl (*data);
469
  return 0;
470
}
471
 
472
static void
473
vapi_request (struct vapi_handler *t)
474
{
475
  unsigned long id, data;
476
 
477
  if (read_packet (t->fd, &id, &data))
478
    {
479
      if (t->fd > 0)
480
        {
481
          perror ("vapi read");
482
          close (t->fd);
483
          t->fd = 0;
484
          rebuild_fds ();
485
        }
486
      return;
487
    }
488
 
489
  vapi_write_log_file (VAPI_COMMAND_REQUEST, id, data);
490
 
491
  /* This packet may be for another handler */
492
  if (!handler_fits_id (t, id))
493
    t = find_handler (id);
494
  if (!t || !t->read_func)
495
    fprintf (stderr,
496
             "WARNING: Received packet for undefined id %08lx, data %08lx\n",
497
             id, data);
498
  else
499
    t->read_func (id, data, t->priv_dat);
500
}
501
 
502
void
503
vapi_check ()
504
{
505
  struct vapi_handler *t;
506
 
507
  if (!server_fd || !fds)
508
    {
509
      fprintf (stderr, "FATAL: Unable to maintain VAPI server.\n");
510
      exit (1);
511
    }
512
 
513
  /* Handle everything in queue. */
514
  while (1)
515
    {
516
      switch (poll (fds, nfds, 0))
517
        {
518
        case -1:
519
          if (errno == EINTR)
520
            continue;
521
          perror ("poll");
522
          if (server_fd)
523
            close (server_fd);
524
          runtime.vapi.enabled = 0;
525
          serverIP = 0;
526
          return;
527
        case 0:          /* Nothing interesting going on */
528
          return;
529
        default:
530
          /* Handle the vapi ports first. */
531
          for (t = vapi_handler; t; t = t->next)
532
            if (t->temp >= 0 && fds[t->temp].revents)
533
              vapi_request (t);
534
 
535
          if (fds[0].revents)
536
            {
537
              if (fds[0].revents & POLLIN)
538
                server_request ();
539
              else
540
                {               /* Error Occurred */
541
                  fprintf (stderr,
542
                           "Received flags 0x%08x on server. Shutting down.\n",
543
                           fds[0].revents);
544
                  if (server_fd)
545
                    close (server_fd);
546
                  server_fd = 0;
547
                  runtime.vapi.enabled = 0;
548
                  serverIP = 0;
549
                }
550
            }
551
          break;
552
        }                       /* End of switch statement */
553
    }                           /* End of while statement */
554
}
555
 
556
/* Inits the VAPI, according to sim-config */
557
int
558
vapi_init ()
559
{
560
  nhandlers = 0;
561
  vapi_handler = NULL;
562
  if (!runtime.vapi.enabled)
563
    return 0;                    /* Nothing to do */
564
 
565
  runtime.vapi.server_port = config.vapi.server_port;
566
  if (!runtime.vapi.server_port)
567
    {
568
      fprintf (stderr, "WARNING: server_port = 0, shutting down VAPI\n");
569
      runtime.vapi.enabled = 0;
570
      return 1;
571
    }
572
  if ((server_fd =
573
       get_server_socket ("or1ksim", "tcp", runtime.vapi.server_port)))
574
    PRINTF ("VAPI Server started on port %d\n", runtime.vapi.server_port);
575
  else
576
    {
577
      perror ("Connection");
578
      return 1;
579
    }
580
 
581
  rebuild_fds ();
582
 
583
  if ((runtime.vapi.vapi_file = fopen (config.vapi.vapi_fn, "wt+")) == NULL)
584
    fprintf (stderr, "WARNING: cannot open VAPI log file\n");
585
 
586
  return 0;
587
}
588
 
589
/* Closes the VAPI */
590
void
591
vapi_done ()
592
{
593
  int i;
594
  struct vapi_handler *t = vapi_handler;
595
 
596
  for (i = 0; i < nfds; i++)
597
    if (fds[i].fd)
598
      close (fds[i].fd);
599
  server_fd = 0;
600
  runtime.vapi.enabled = 0;
601
  serverIP = 0;
602
  free (fds);
603
  fds = 0;
604
  if (runtime.vapi.vapi_file)
605
    {
606
      /* Mark end of simulation */
607
      vapi_write_log_file (VAPI_COMMAND_END, 0, 0);
608
      fclose (runtime.vapi.vapi_file);
609
    }
610
 
611
  while (vapi_handler)
612
    {
613
      t = vapi_handler;
614
      vapi_handler = vapi_handler->next;
615
      free (t);
616
    }
617
}
618
 
619
/* Installs a vapi handler for one VAPI id */
620
void
621
vapi_install_handler (unsigned long id,
622
                      void (*read_func) (unsigned long, unsigned long,
623
                                         void *), void *dat)
624
{
625
  vapi_install_multi_handler (id, 1, read_func, dat);
626
}
627
 
628
/* Installs a vapi handler for many VAPI id */
629
void
630
vapi_install_multi_handler (unsigned long base_id, unsigned long num_ids,
631
                            void (*read_func) (unsigned long, unsigned long,
632
                                               void *), void *dat)
633
{
634
  struct vapi_handler *tt;
635
 
636
  if (read_func == NULL)
637
    {
638
      struct vapi_handler **t = &vapi_handler;
639
      while ((*t) && !handler_fits_id (*t, base_id))
640
        t = &(*t)->next;
641
      if (!t)
642
        {
643
          fprintf (stderr, "Cannot uninstall VAPI read handler from id %lx\n",
644
                   base_id);
645
          exit (1);
646
        }
647
      tt = *t;
648
      (*t) = (*t)->next;
649
      free (tt);
650
      nhandlers--;
651
    }
652
  else
653
    {
654
      tt = find_handler (base_id);
655
      if (!tt)
656
        {
657
          tt = add_handler (base_id, num_ids);
658
          tt->read_func = read_func;
659
          tt->priv_dat = dat;
660
        }
661
      else
662
        {
663
          tt->read_func = read_func;
664
          tt->priv_dat = dat;
665
          rebuild_fds ();
666
        }
667
    }
668
}
669
 
670
/* Returns number of unconnected handles.  */
671
int
672
vapi_num_unconnected (int printout)
673
{
674
  struct vapi_handler *t = vapi_handler;
675
  int numu = 0;
676
  for (; t; t = t->next)
677
    {
678
      if (!t->fd)
679
        {
680
          numu++;
681
          if (printout)
682
            {
683
              if (t->num_ids == 1)
684
                PRINTF (" 0x%lx", t->base_id);
685
              else
686
                PRINTF (" 0x%lx..0x%lx", t->base_id,
687
                        t->base_id + t->num_ids - 1);
688
            }
689
        }
690
    }
691
  return numu;
692
}
693
 
694
/* Sends a packet to specified test */
695
void
696
vapi_send (unsigned long id, unsigned long data)
697
{
698
  vapi_write_log_file (VAPI_COMMAND_SEND, id, data);
699
  write_packet (id, data);
700
}
701
 
702
/*
703
int main ()
704
{
705
  runtime.vapi.enabled = 1;
706
  config.vapi.server_port = 9999;
707
  vapi_init ();
708
  while (1) {
709
    vapi_check();
710
    usleep(1);
711
  }
712
  vapi_done ();
713
}*/
714
 
715
/*---------------------------------------------------[ VAPI configuration ]---*/
716
 
717
static void
718
vapi_enabled (union param_val val, void *dat)
719
{
720
  config.vapi.enabled = val.int_val;
721
}
722
 
723
 
724
/*---------------------------------------------------------------------------*/
725
/*!Set the VAPI server port
726
 
727
   Ensure the value chosen is valid
728
 
729
   @param[in] val  The value to use
730
   @param[in] dat  The config data structure (not used here)                 */
731
/*---------------------------------------------------------------------------*/
732
static void
733
vapi_server_port (union param_val val, void *dat)
734
{
735
  if ((val.int_val < 1) || (val.int_val > 65535))
736
    {
737
      fprintf (stderr, "Warning: invalid VAPI port specified: ignored\n");
738
    }
739
  else
740
    {
741
      config.vapi.server_port = val.int_val;
742
    }
743
}       /* vapi_server_port() */
744
 
745
 
746
static void
747
vapi_log_enabled (union param_val val, void *dat)
748
{
749
  config.vapi.log_enabled = val.int_val;
750
}
751
 
752
static void
753
vapi_hide_device_id (union param_val val, void *dat)
754
{
755
  config.vapi.hide_device_id = val.int_val;
756
}
757
 
758
 
759
/*---------------------------------------------------------------------------*/
760
/*!Set the log file
761
 
762
   Free any existing string.
763
 
764
   @param[in] val  The value to use
765
   @param[in] dat  The config data structure (not used here)                 */
766
/*---------------------------------------------------------------------------*/
767
static void
768
vapi_log_fn (union param_val  val,
769
             void            *dat)
770
{
771
  if (NULL != config.vapi.vapi_fn)
772
    {
773
      free (config.vapi.vapi_fn);
774
    }
775
 
776
  config.vapi.vapi_fn = strdup (val.str_val);
777
 
778
}       /* vapi_log_fn() */
779
 
780
 
781
void
782
reg_vapi_sec (void)
783
{
784
  struct config_section *sec = reg_config_sec ("VAPI", NULL, NULL);
785
 
786 224 jeremybenn
  reg_config_param (sec, "enabled",        PARAMT_INT, vapi_enabled);
787
  reg_config_param (sec, "server_port",    PARAMT_INT, vapi_server_port);
788
  reg_config_param (sec, "log_enabled",    PARAMT_INT, vapi_log_enabled);
789
  reg_config_param (sec, "hide_device_id", PARAMT_INT, vapi_hide_device_id);
790
  reg_config_param (sec, "vapi_log_file",  PARAMT_STR, vapi_log_fn);
791
  reg_config_param (sec, "vapi_log_fn",    PARAMT_STR, vapi_log_fn);
792 19 jeremybenn
}

powered by: WebSVN 2.1.0

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