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

Subversion Repositories or1k

[/] [or1k/] [branches/] [stable_0_2_x/] [or1ksim/] [debug/] [gdbcomm.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 321 markom
/* gdbcomm.c -- Communication routines for gdb
2 479 markom
         Copyright (C) 2001 by Marko Mlinar, markom@opencores.org
3
         Code copied from toplevel.c
4 321 markom
 
5 479 markom
         This file is part of OpenRISC 1000 Architectural Simulator.
6
 
7
         This program is free software; you can redistribute it and/or modify
8
         it under the terms of the GNU General Public License as published by
9
         the Free Software Foundation; either version 2 of the License, or
10
         (at your option) any later version.
11
 
12
         This program is distributed in the hope that it will be useful,
13
         but WITHOUT ANY WARRANTY; without even the implied warranty of
14
         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
15
         GNU General Public License for more details.
16 321 markom
 
17 479 markom
         You should have received a copy of the GNU General Public License
18
         along with this program; if not, write to the Free Software
19
         Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 321 markom
*/
21
 
22
#include <stdlib.h>
23
#include <stdio.h>
24
#include <sys/stat.h>
25
#include <sys/types.h>
26
#include <sys/socket.h>
27
#include <netinet/in.h>
28
#include <sys/select.h>
29
#include <sys/poll.h>
30
#include <fcntl.h>
31
#include <netdb.h>
32
#include <netinet/tcp.h>
33 1308 phoenix
#include <unistd.h>
34
#include <string.h>
35 321 markom
 
36 1358 nogj
#include "config.h"
37
 
38
#ifdef HAVE_INTTYPES_H
39
#include <inttypes.h>
40
#endif
41
 
42
#include "port.h"
43
#include "arch.h"
44 321 markom
#include "gdb.h"
45
#include "gdbcomm.h"
46 439 erez
#include "vapi.h"
47 321 markom
#include "sim-config.h"
48 1308 phoenix
#include "debug_unit.h"
49 321 markom
 
50 1308 phoenix
static int gdb_read(void* buf,int len);
51
static int gdb_write(const void* buf,int len);
52
 
53 321 markom
static unsigned int serverIP = 0;
54
static unsigned int serverPort = 0;
55
static unsigned int server_fd = 0;
56
static unsigned int gdb_fd = 0;
57
 
58
static int tcp_level = 0;
59
 
60
/* Added by CZ 24/05/01 */
61
int GetServerSocket(const char* name,const char* proto,int port)
62
{
63
  struct servent *service;
64
  struct protoent *protocol;
65
  struct sockaddr_in sa;
66
  struct hostent *hp;
67
  int sockfd;
68
  char myname[256];
69
  int flags;
70
  char sTemp[256];
71 1557 nogj
  socklen_t len;
72 321 markom
 
73
  /* First, get the protocol number of TCP */
74
  if(!(protocol = getprotobyname(proto)))
75
    {
76
      sprintf(sTemp,"Unable to load protocol \"%s\"",proto);
77
      perror(sTemp);
78
      return 0;
79
    }
80
  tcp_level = protocol->p_proto; /* Save for later */
81
 
82
  /* If we weren't passed a non standard port, get the port
83
     from the services directory. */
84
  if(!port)
85
    {
86 1308 phoenix
      if((service = getservbyname(name,protocol->p_name)))
87 479 markom
        port = ntohs(service->s_port);
88 321 markom
    }
89
 
90
  /* Create the socket using the TCP protocol */
91
  if((sockfd = socket(PF_INET,SOCK_STREAM,protocol->p_proto)) < 0)
92
    {
93
      perror("Unable to create socket");
94
      return 0;
95
    }
96
 
97
  flags = 1;
98
  if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const char*)&flags,sizeof(int)) < 0)
99
    {
100
      sprintf(sTemp,"Can not set SO_REUSEADDR option on socket %d",sockfd);
101
      perror(sTemp);
102
      close(sockfd);
103
      return 0;
104
    }
105
 
106
  /* The server should also be non blocking. Get the current flags. */
107
  if(fcntl(sockfd,F_GETFL,&flags) < 0)
108
    {
109
      sprintf(sTemp,"Unable to get flags for socket %d",sockfd);
110
      perror(sTemp);
111
      close(sockfd);
112
      return 0;
113
    }
114
 
115
  /* Set the nonblocking flag */
116
  if(fcntl(sockfd,F_SETFL, flags | O_NONBLOCK) < 0)
117
    {
118
      sprintf(sTemp,"Unable to set flags for socket %d to value 0x%08x",
119 479 markom
              sockfd,flags | O_NONBLOCK);
120 321 markom
      perror(sTemp);
121
      close(sockfd);
122
      return 0;
123
    }
124
 
125
  /* Find out what our address is */
126
  memset(&sa,0,sizeof(struct sockaddr_in));
127
  gethostname(myname,sizeof(myname));
128
  if(!(hp = gethostbyname(myname)))
129
    {
130
      perror("Unable to read hostname");
131
      close(sockfd);
132
      return 0;
133
    }
134
 
135
  /* Bind our socket to the appropriate address */
136
  sa.sin_family = hp->h_addrtype;
137
  sa.sin_port = htons(port);
138
  if(bind(sockfd,(struct sockaddr*)&sa,sizeof(struct sockaddr_in)) < 0)
139
    {
140
      sprintf(sTemp,"Unable to bind socket %d to port %d",sockfd,port);
141
      perror(sTemp);
142
      close(sockfd);
143
      return 0;
144
    }
145
  serverIP = sa.sin_addr.s_addr;
146 1557 nogj
  len = sizeof(struct sockaddr_in);
147
  if(getsockname(sockfd,(struct sockaddr*)&sa,&len) < 0)
148 321 markom
    {
149
      sprintf(sTemp,"Unable to get socket information for socket %d",sockfd);
150
      perror(sTemp);
151
      close(sockfd);
152
      return 0;
153
    }
154
  serverPort = ntohs(sa.sin_port);
155
 
156
  /* Set the backlog to 1 connections */
157
  if(listen(sockfd,1) < 0)
158
    {
159
      sprintf(sTemp,"Unable to set backlog on socket %d to %d",sockfd,1);
160
      perror(sTemp);
161
      close(sockfd);
162
      return 0;
163
    }
164
 
165
  return sockfd;
166
}
167
 
168 1550 nogj
void BlockJTAG(void)
169 321 markom
{
170
  struct pollfd fds[2];
171
  int n = 0;
172
 
173
  fds[n].fd = server_fd;
174
  fds[n].events = POLLIN;
175
  fds[n++].revents = 0;
176
  if(gdb_fd)
177
    {
178
      fds[n].fd = gdb_fd;
179
      fds[n].events = POLLIN;
180
      fds[n++].revents = 0;
181
    }
182
  poll(fds,n,-1);
183
}
184
 
185
void HandleServerSocket(Boolean block)
186
{
187
  struct pollfd fds[3];
188
  int n = 0;
189
  int timeout = block ? -1 : 0;
190
  Boolean data_on_stdin = false;
191
  int o_serv_fd = server_fd;
192
 
193
  if(!o_serv_fd && !gdb_fd)
194
    return;
195
 
196
  if(o_serv_fd)
197
    {
198
      fds[n].fd = o_serv_fd;
199
      fds[n].events = POLLIN;
200
      fds[n++].revents = 0;
201
    }
202
  if(gdb_fd)
203
    {
204
      fds[n].fd = gdb_fd;
205
      fds[n].events = POLLIN;
206
      fds[n++].revents = 0;
207
    }
208
  if(block)
209
    {
210
      fds[n].fd = 0;
211
      fds[n].events = POLLIN;
212
      fds[n++].revents = 0;
213
    }
214
 
215
  while(!data_on_stdin)
216
    {
217
      switch(poll(fds,n,timeout))
218 479 markom
        {
219
        case -1:
220
          if(errno == EINTR)
221
            continue;
222
          perror("poll");
223
          server_fd = 0;
224
          break;
225
        case 0: /* Nothing interesting going on */
226
          data_on_stdin = true; /* Can only get here if nonblocking */
227
          break;
228
        default:
229
          /* Make sure to handle the gdb port first! */
230 1557 nogj
          if((fds[0].revents && gdb_fd && !o_serv_fd) ||
231
             (fds[1].revents && server_fd && gdb_fd))
232 479 markom
            {
233
              int revents = o_serv_fd ? fds[1].revents : fds[0].revents;
234 321 markom
 
235 479 markom
              if(revents & POLLIN)
236
                GDBRequest();
237
              else /* Error Occurred */
238
                {
239
                  fprintf(stderr,"Received flags 0x%08x on gdb socket. Shutting down.\n",revents);
240
                  close(gdb_fd);
241
                  gdb_fd = 0;
242
                }
243
            }
244
          if(fds[0].revents && o_serv_fd)
245
            {
246
              if(fds[0].revents & POLLIN)
247
                JTAGRequest();
248
              else /* Error Occurred */
249
                {
250
                  fprintf(stderr,"Received flags 0x%08x on server. Shutting down.\n",fds[0].revents);
251
                  close(o_serv_fd);
252
                  server_fd = 0;
253
                  serverPort = 0;
254
                  serverIP = 0;
255
                }
256
            }
257
          if(fds[2].revents || (fds[1].revents && !gdb_fd))
258
            data_on_stdin = true;
259
          break;
260
        } /* End of switch statement */
261 321 markom
    } /* End of while statement */
262
}
263
 
264 1550 nogj
void JTAGRequest(void)
265 321 markom
{
266
  struct sockaddr_in sa;
267
  struct sockaddr* addr = (struct sockaddr*)&sa;
268 1557 nogj
  socklen_t len = sizeof(struct sockaddr_in);
269
  int fd = accept(server_fd,addr,&len);
270 321 markom
  int on_off = 0; /* Turn off Nagel's algorithm on the socket */
271
  int flags;
272
  char sTemp[256];
273
 
274
  if(fd < 0)
275
    {
276
      /* This is valid, because a connection could have started,
277 479 markom
         and then terminated due to a protocol error or user
278
         initiation before the accept could take place. */
279 321 markom
      if(errno != EWOULDBLOCK && errno != EAGAIN)
280 479 markom
        {
281
          perror("accept");
282
          close(server_fd);
283
          server_fd = 0;
284
          serverPort = 0;
285
          serverIP = 0;
286
        }
287 321 markom
      return;
288
    }
289
 
290
  if(gdb_fd)
291
    {
292
      close(fd);
293
      return;
294
    }
295
 
296
  if(fcntl(fd,F_GETFL,&flags) < 0)
297
    {
298
      sprintf(sTemp,"Unable to get flags for gdb socket %d",fd);
299
      perror(sTemp);
300
      close(fd);
301
      return;
302
    }
303
 
304
  if(fcntl(fd,F_SETFL, flags | O_NONBLOCK) < 0)
305
    {
306
      sprintf(sTemp,"Unable to set flags for gdb socket %d to value 0x%08x",
307 479 markom
              fd,flags | O_NONBLOCK);
308 321 markom
      perror(sTemp);
309
      close(fd);
310
      return;
311
    }
312
 
313
  if(setsockopt(fd,tcp_level,TCP_NODELAY,&on_off,sizeof(int)) < 0)
314
    {
315
      sprintf(sTemp,"Unable to disable Nagel's algorithm for socket %d.\nsetsockopt",fd);
316
      perror(sTemp);
317
      close(fd);
318
      return;
319
    }
320
 
321
  gdb_fd = fd;
322
}
323
 
324 1550 nogj
void GDBRequest(void)
325 321 markom
{
326
  JTAGProxyWriteMessage msg_write;
327
  JTAGProxyReadMessage msg_read;
328
  JTAGProxyChainMessage msg_chain;
329
  JTAGProxyWriteResponse resp_write;
330
  JTAGProxyReadResponse resp_read;
331
  JTAGProxyChainResponse resp_chain;
332
  JTAGProxyBlockWriteMessage *msg_bwrite;
333
  JTAGProxyBlockReadMessage msg_bread;
334
  JTAGProxyBlockWriteResponse resp_bwrite;
335
  JTAGProxyBlockReadResponse *resp_bread;
336
  char *buf;
337
  int err = 0;
338
  uint32_t command,length;
339
  int len,i;
340
 
341
  /* First, we must read the incomming command */
342
  if(gdb_read(&command,sizeof(uint32_t)) < 0)
343
    {
344
      if(gdb_fd)
345 479 markom
        {
346
          perror("gdb socket - 1");
347
          close(gdb_fd);
348
          gdb_fd = 0;
349
        }
350 321 markom
      return;
351
    }
352
  if(gdb_read(&length,sizeof(uint32_t)) < 0)
353
    {
354
      if(gdb_fd)
355 479 markom
        {
356
          perror("gdb socket - 2");
357
          close(gdb_fd);
358
          gdb_fd = 0;
359
        }
360 321 markom
      return;
361
    }
362
  length = ntohl(length);
363
 
364
  /* Now, verify the protocol and implement the command */
365
  switch(ntohl(command))
366
    {
367
    case JTAG_COMMAND_WRITE:
368
      if(length != sizeof(msg_write) - 8)
369 479 markom
        {
370
          ProtocolClean(length,JTAG_PROXY_PROTOCOL_ERROR);
371
          return;
372
        }
373 321 markom
      buf = (char*)&msg_write;
374
      if(gdb_read(&buf[8],length) < 0)
375 479 markom
        {
376
          if(gdb_fd)
377
            {
378
              perror("gdb socket - 3");
379
              close(gdb_fd);
380
              gdb_fd = 0;
381
            }
382
          return;
383
        }
384 321 markom
      msg_write.address = ntohl(msg_write.address);
385
      msg_write.data_H = ntohl(msg_write.data_H);
386
      msg_write.data_L = ntohl(msg_write.data_L);
387
      err = DebugSetRegister(msg_write.address,msg_write.data_L);
388
      resp_write.status = htonl(err);
389
      if(gdb_write(&resp_write,sizeof(resp_write)) < 0)
390 479 markom
        {
391
          if(gdb_fd)
392
            {
393
              perror("gdb socket - 4");
394
              close(gdb_fd);
395
              gdb_fd = 0;
396
            }
397
          return;
398
        }
399 321 markom
      break;
400
    case JTAG_COMMAND_READ:
401
      if(length != sizeof(msg_read) - 8)
402 479 markom
        {
403
          ProtocolClean(length,JTAG_PROXY_PROTOCOL_ERROR);
404
          return;
405
        }
406 321 markom
      buf = (char*)&msg_read;
407
      if(gdb_read(&buf[8],length) < 0)
408 479 markom
        {
409
          if(gdb_fd)
410
            {
411
              perror("gdb socket - 5");
412
              close(gdb_fd);
413
              gdb_fd = 0;
414
            }
415
          return;
416
        }
417 321 markom
      msg_read.address = ntohl(msg_read.address);
418
      err = DebugGetRegister(msg_read.address,&resp_read.data_L);
419
      resp_read.status = htonl(err);
420
      resp_read.data_H = 0;
421
      resp_read.data_L = htonl(resp_read.data_L);
422
      if(gdb_write(&resp_read,sizeof(resp_read)) < 0)
423 479 markom
        {
424
          if(gdb_fd)
425
            {
426
              perror("gdb socket - 6");
427
              close(gdb_fd);
428
              gdb_fd = 0;
429
            }
430
          return;
431
        }
432 321 markom
      break;
433
    case JTAG_COMMAND_BLOCK_WRITE:
434
      if(length < sizeof(JTAGProxyBlockWriteMessage)-8)
435 479 markom
        {
436
          ProtocolClean(length,JTAG_PROXY_PROTOCOL_ERROR);
437
          return;
438
        }
439 321 markom
      if(!(buf = (char*)malloc(8+length)))
440 479 markom
        {
441
          ProtocolClean(length,JTAG_PROXY_OUT_OF_MEMORY);
442
          return;
443
        }
444 321 markom
      msg_bwrite = (JTAGProxyBlockWriteMessage*)buf;
445
      if(gdb_read(&buf[8],length) < 0)
446 479 markom
        {
447
          if(gdb_fd)
448
            {
449
              perror("gdb socket - 5");
450
              close(gdb_fd);
451
              gdb_fd = 0;
452
            }
453
          free(buf);
454
          return;
455
        }
456 321 markom
      msg_bwrite->address = ntohl(msg_bwrite->address);
457
      msg_bwrite->nRegisters = ntohl(msg_bwrite->nRegisters);
458
      for(i=0;i<msg_bwrite->nRegisters;i++)
459 479 markom
        {
460
          int t_err = 0;
461 321 markom
 
462 479 markom
          msg_bwrite->data[i] = ntohl(msg_bwrite->data[i]);
463
          t_err = DebugSetRegister(msg_bwrite->address + 4 * i,msg_bwrite->data[i]);
464
          err = err ? err : t_err;
465
        }
466 321 markom
      resp_bwrite.status = htonl(err);
467
      free(buf);
468
      buf = NULL;
469
      msg_bwrite = NULL;
470
      if(gdb_write(&resp_bwrite,sizeof(resp_bwrite)) < 0)
471 479 markom
        {
472
          if(gdb_fd)
473
            {
474
              perror("gdb socket - 4");
475
              close(gdb_fd);
476
              gdb_fd = 0;
477
            }
478
          return;
479
        }
480 321 markom
      break;
481
    case JTAG_COMMAND_BLOCK_READ:
482
      if(length != sizeof(msg_bread) - 8)
483 479 markom
        {
484
          ProtocolClean(length,JTAG_PROXY_PROTOCOL_ERROR);
485
          return;
486
        }
487 321 markom
      buf = (char*)&msg_bread;
488
      if(gdb_read(&buf[8],length) < 0)
489 479 markom
        {
490
          if(gdb_fd)
491
            {
492
              perror("gdb socket - 5");
493
              close(gdb_fd);
494
              gdb_fd = 0;
495
            }
496
          return;
497
        }
498 321 markom
      msg_bread.address = ntohl(msg_bread.address);
499
      msg_bread.nRegisters = ntohl(msg_bread.nRegisters);
500
      len = sizeof(JTAGProxyBlockReadResponse) + 4*(msg_bread.nRegisters-1);
501
      if(!(buf = (char*)malloc(len)))
502 479 markom
        {
503
          ProtocolClean(0,JTAG_PROXY_OUT_OF_MEMORY);
504
          return;
505
        }
506 321 markom
      resp_bread = (JTAGProxyBlockReadResponse*)buf;
507
      for(i=0;i<msg_bread.nRegisters;i++)
508 479 markom
        {
509
          int t_err;
510 321 markom
 
511 479 markom
          t_err = DebugGetRegister(msg_bread.address + 4 * i,&resp_bread->data[i]);
512
          resp_bread->data[i] = htonl(resp_bread->data[i]);
513
          err = err ? err : t_err;
514
        }
515 321 markom
      resp_bread->status = htonl(err);
516
      resp_bread->nRegisters = htonl(msg_bread.nRegisters);
517
      if(gdb_write(resp_bread,len) < 0)
518 479 markom
        {
519
          if(gdb_fd)
520
            {
521
              perror("gdb socket - 6");
522
              close(gdb_fd);
523
              gdb_fd = 0;
524
            }
525
          free(buf);
526
          return;
527
        }
528 321 markom
      free(buf);
529
      buf = NULL;
530
      resp_bread = NULL;
531
      break;
532
    case JTAG_COMMAND_CHAIN:
533
      if(length != sizeof(msg_chain) - 8)
534 479 markom
        {
535
          ProtocolClean(length,JTAG_PROXY_PROTOCOL_ERROR);
536
          return;
537
        }
538 321 markom
      buf = (char*)&msg_chain;
539
      if(gdb_read(&buf[8],sizeof(msg_chain)-8) < 0)
540 479 markom
        {
541
          if(gdb_fd)
542
            {
543
              perror("gdb socket - 7");
544
              close(gdb_fd);
545
              gdb_fd = 0;
546
            }
547
          return;
548
        }
549 321 markom
      msg_chain.chain = htonl(msg_chain.chain);
550
      err = DebugSetChain(msg_chain.chain);
551
      resp_chain.status = htonl(err);
552
      if(gdb_write(&resp_chain,sizeof(resp_chain)) < 0)
553 479 markom
        {
554
          if(gdb_fd)
555
            {
556
              perror("gdb socket - 8");
557
              close(gdb_fd);
558
              gdb_fd = 0;
559
            }
560
          return;
561
        }
562 321 markom
      break;
563
    default:
564
      ProtocolClean(length,JTAG_PROXY_COMMAND_NOT_IMPLEMENTED);
565
      break;
566
    }
567
}
568
 
569
void ProtocolClean(int length,int32_t err)
570
{
571
  char buf[4096];
572
 
573
  err = htonl(err);
574
  if((gdb_read(buf,length) < 0) ||
575 1557 nogj
      ((gdb_write(&err,sizeof(err)) < 0) && gdb_fd))
576 321 markom
    {
577
      perror("gdb socket - 9");
578
      close(gdb_fd);
579
      gdb_fd = 0;
580
    }
581
}
582
 
583 439 erez
static int gdb_write(const void* buf,int len)
584 321 markom
{
585 439 erez
  int n, log_n = 0;
586
  const char* w_buf = (const char*)buf;
587
  const uint32_t* log_buf = (const uint32_t*)buf;
588 321 markom
  struct pollfd block;
589
 
590 439 erez
  while(len) {
591
    if((n = write(gdb_fd,w_buf,len)) < 0) {
592
      switch(errno) {
593
      case EWOULDBLOCK: /* or EAGAIN */
594 479 markom
        /* We've been called on a descriptor marked
595
           for nonblocking I/O. We better simulate
596
           blocking behavior. */
597
        block.fd = gdb_fd;
598
        block.events = POLLOUT;
599
        block.revents = 0;
600
        poll(&block,1,-1);
601
        continue;
602 439 erez
      case EINTR:
603 479 markom
        continue;
604 439 erez
      case EPIPE:
605 479 markom
        close(gdb_fd);
606
        gdb_fd = 0;
607
        return -1;
608 439 erez
      default:
609 479 markom
        return -1;
610 439 erez
      }
611 321 markom
    }
612 439 erez
    else {
613
      len -= n;
614
      w_buf += n;
615
      if ( config.debug.vapi_id )
616 479 markom
        for ( log_n += n; log_n >= 4; log_n -= 4, ++ log_buf )
617
          vapi_write_log_file( VAPI_COMMAND_SEND, config.debug.vapi_id, ntohl(*log_buf) );
618 439 erez
    }
619
  }
620 321 markom
  return 0;
621
}
622
 
623
static int gdb_read(void* buf,int len)
624
{
625 439 erez
  int n, log_n = 0;
626 321 markom
  char* r_buf = (char*)buf;
627 439 erez
  uint32_t* log_buf = (uint32_t*)buf;
628 321 markom
  struct pollfd block;
629
 
630 439 erez
  while(len) {
631
    if((n = read(gdb_fd,r_buf,len)) < 0) {
632
      switch(errno) {
633
      case EWOULDBLOCK: /* or EAGAIN */
634 479 markom
        /* We've been called on a descriptor marked
635
           for nonblocking I/O. We better simulate
636
           blocking behavior. */
637
        block.fd = gdb_fd;
638
        block.events = POLLIN;
639
        block.revents = 0;
640
        poll(&block,1,-1);
641
        continue;
642 439 erez
      case EINTR:
643 479 markom
        continue;
644 439 erez
      default:
645 479 markom
        return -1;
646 439 erez
      }
647 321 markom
    }
648 439 erez
    else if(n == 0) {
649
      close(gdb_fd);
650
      gdb_fd = 0;
651
      return -1;
652
    }
653
    else {
654
      len -= n;
655
      r_buf += n;
656
      if ( config.debug.vapi_id )
657 479 markom
        for ( log_n += n; log_n >= 4; log_n -= 4, ++ log_buf )
658
          vapi_write_log_file( VAPI_COMMAND_REQUEST, config.debug.vapi_id, ntohl(*log_buf) );
659 439 erez
    }
660
  }
661 321 markom
  return 0;
662
}
663
 
664 1557 nogj
void gdbcomm_init (void)
665 321 markom
{
666
  serverPort = config.debug.server_port;
667 1308 phoenix
  if((server_fd = GetServerSocket("or1ksim","tcp",serverPort)))
668 997 markom
    PRINTF("JTAG Proxy server started on port %d\n",serverPort);
669 479 markom
  else
670 997 markom
    PRINTF("Cannot start JTAG proxy server on port %d\n", serverPort);
671 321 markom
}

powered by: WebSVN 2.1.0

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