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

Subversion Repositories eco32

[/] [eco32/] [trunk/] [fp/] [implementation/] [mmix/] [mmmix.w] - Blame information for rev 15

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 15 hellwig
% This file is part of the MMIXware package (c) Donald E Knuth 1999
2
@i boilerplate.w %<< legal stuff: PLEASE READ IT BEFORE MAKING ANY CHANGES!
3
 
4
\def\title{MMMIX}
5
\def\MMIX{\.{MMIX}}
6
\def\Hex#1{\hbox{$^{\scriptscriptstyle\#}$\tt#1}} % experimental hex constant
7
@s octa int
8
@s tetra int
9
@s bool int
10
@s fetch int
11
@s specnode int
12
 
13
@* Introduction.
14
This \.{CWEB} program simulates how the \MMIX\ computer might be
15
implemented with a high-performance pipeline in many different configurations.
16
All of the complexities of \MMIX's architecture are treated, except for
17
multiprocessing and low-level details of memory mapped input/output.
18
 
19
The present program module, which contains the main routine for the
20
\MMIX\ meta-simulator, is primarily devoted to administrative tasks. Other modules
21
do the actual work after this module has told them what to do.
22
 
23
@ A user typically invokes the meta-simulator with a \UNIX/-like command line
24
of the general form
25
`\.{mmmix}~\.{configfile}~\.{progfile}',
26
where the \.{configfile} describes the characteristics
27
of an \MMIX\ implementation and the \.{progfile} contains a program to
28
be downloaded and run. Rules for configuration files appear in
29
the module called \.{mmix-config}. The program file is either
30
an ``\MMIX\ binary file'' dumped by {\mc MMIX-SIM}, or an
31
ASCII text file that describes hexadecimal data
32
in a rudimentary format. It is assumed to be binary if
33
its name ends with the extension `\.{.mmb}'.
34
 
35
@c
36
#include 
37
#include 
38
#include 
39
#include "mmix-pipe.h"
40
@#
41
char *config_file_name, *prog_file_name;
42
@@;
43
@@;
44
 
45
int main(argc,argv)
46
  int argc;
47
  char *argv[];
48
{
49
  @;
50
  MMIX_config(config_file_name);
51
  MMIX_init();
52
  mmix_io_init();
53
  @;
54
  @;
55
  printf("Simulation ended at time %d.\n",ticks.l);
56
  print_stats();
57
  return 0;
58
}
59
 
60
@ The command line might also contain options, some day.
61
For now I'm forgetting them and simplifying everything until I gain
62
further experience.
63
 
64
@=
65
if (argc!=3) {
66
  fprintf(stderr,"Usage: %s configfile progfile\n",argv[0]);
67
@.Usage: ...@>
68
  exit(-3);
69
}
70
config_file_name=argv[1];
71
prog_file_name=argv[2];
72
 
73
@ @=
74
if (strlen(prog_file_name)>4 &&
75
     strcmp(prog_file_name+strlen(prog_file_name)-4,".mmb")==0)
76
  @@;
77
else @;
78
fclose(prog_file);
79
 
80
@* Hexadecimal input to memory.
81
A rudimentary hexadecimal input format is implemented here so that the
82
@^hexadecimal files@>
83
simulator can be run with essentially arbitrary data in the simulated memory.
84
The rules of this format are extremely simple: Each line of the file
85
either begins with (i)~12 hexadecimal digits followed by a colon; or
86
(ii)~a space followed by 16 hexadecimal digits. In case~(i), the 12
87
hex digits specify a 48-bit physical address, called the current
88
location. In case~(ii), the 16 hex digits specify an octabyte to be
89
stored in the current location; the current location is then increased by~8.
90
The current location should be a multiple of~8, but its three least
91
significant bits are actually ignored. Arbitrary comments can follow
92
the specification of a new current location or a new octabyte, as long
93
as each line is less than 99 characters long. For example, the file
94
$$\vbox{\halign{\tt#\hfil\cr
95
0123456789ab: SILLY EXAMPLE\cr
96
\ 0123456789abcdef first octabyte\cr
97
\ fedbca9876543210 second\cr}}$$
98
places the octabyte
99
\Hex{0123456789abcdef} into memory location \Hex{0123456789a8}
100
and \Hex{fedcba9876543210} into location \Hex{0123456789b0}.
101
 
102
@d BUF_SIZE 100
103
 
104
@=
105
octa cur_loc;
106
octa cur_dat;
107
bool new_chunk;
108
char buffer[BUF_SIZE];
109
FILE *prog_file;
110
 
111
@ @=
112
{
113
  prog_file=fopen(prog_file_name,"r");
114
  if (!prog_file) {
115
    fprintf(stderr,"Panic: Can't open MMIX hexadecimal file %s!\n",prog_file_name);
116
@.Can't open...@>
117
    exit(-3);
118
  }
119
  new_chunk=true;
120
  while (1) {
121
    if (!fgets(buffer,BUF_SIZE,prog_file)) break;
122
    if (buffer[strlen(buffer)-1]!='\n') {
123
      fprintf(stderr,"Panic: Hexadecimal file line too long: `%s...'!\n",buffer);
124
@.Hexadecimal file line...@>
125
      exit(-3);
126
    }
127
    if (buffer[12]==':') @@;
128
    else if (buffer[0]==' ') @@;
129
    else {
130
      fprintf(stderr,"Panic: Improper hexadecimal file line: `%s'!\n",buffer);
131
@.Improper hexadecimal...@>
132
      exit(-3);
133
    }
134
  }
135
}
136
 
137
@ @=
138
{
139
  if (sscanf(buffer,"%4x%8x",&cur_loc.h,&cur_loc.l)!=2) {
140
    fprintf(stderr,"Panic: Improper hexadecimal file location: `%s'!\n",buffer);
141
@.Improper hexadecimal...@>
142
    exit(-3);
143
  }
144
  new_chunk=true;
145
}
146
 
147
@ @=
148
{
149
  if (sscanf(buffer+1,"%8x%8x",&cur_dat.h,&cur_dat.l)!=2) {
150
    fprintf(stderr,"Panic: Improper hexadecimal file data: `%s'!\n",buffer);
151
@.Improper hexadecimal...@>
152
    exit(-3);
153
  }
154
  if (new_chunk) mem_write(cur_loc,cur_dat);
155
  else mem_hash[last_h].chunk[(cur_loc.l&0xffff)>>3]=cur_dat;
156
  cur_loc.l+=8;
157
  if ((cur_loc.l&0xfff8)!=0) new_chunk=false;
158
  else {
159
    new_chunk=true;
160
    if ((cur_loc.l&0xffff0000)==0) cur_loc.h++;
161
  }
162
}
163
 
164
@* Binary input to memory.
165
When the program file was dumped by {\mc MMIX-SIM}, it
166
has the simple format discussed in exercise 1.4.3$'$--20 of the \MMIX\ fascicle.
167
@^binary files@>
168
@^segments@>
169
In this case we assume that the user's program has text, data, pool, and stack
170
segments, as in the conventions of that book.
171
We load it into four
172
$2^{32}$-byte pages of physical memory, one for each segment; page zero of
173
segment~$i$ is mapped to physical location $2^{32}i$. Page tables are kept in
174
physical locations starting at $2^{32}\times4$; static traps begin at
175
$2^{32}\times 5$ and dynamic traps at $2^{32}\times6$. (These conventions
176
agree with the special register settings
177
$\rm rT=\Hex{8000000500000000}$,
178
$\rm rTT=\Hex{8000000600000000}$,
179
$\rm rV=\Hex{369c200400000000}$
180
assumed by the stripped-down simulator.)
181
 
182
@=
183
{
184
  prog_file=fopen(prog_file_name,"rb");
185
  if (!prog_file) {
186
    fprintf(stderr,"Panic: Can't open MMIX binary file %s!\n",prog_file_name);
187
@.Can't open...@>
188
    exit(-3);
189
  }
190
  while (1) {
191
    if (!undump_octa()) break;
192
    new_chunk=true;
193
    cur_loc=cur_dat;
194
    if (cur_loc.h&0x9fffffff) bad_address=true;
195
    else bad_address=false, cur_loc.h >>= 29;
196
         /* apply trivial mapping function for each segment */
197
    @;
198
  }
199
  @;
200
}
201
 
202
@ The |undump_octa| routine reads eight bytes from the binary file
203
|prog_file| into the global octabyte |cur_dat|,
204
taking care as usual to be big-endian regardless of the host computer's bias.
205
@^big-endian versus little-endian@>
206
@^little-endian versus big-endian@>
207
 
208
@=
209
static bool undump_octa @,@,@[ARGS((void))@];@+@t}\6{@>
210
static bool undump_octa()
211
{
212
  register int t0,t1,t2,t3;
213
  t0=fgetc(prog_file);@+ if (t0==EOF) return false;
214
  t1=fgetc(prog_file);@+ if (t1==EOF) goto oops;
215
  t2=fgetc(prog_file);@+ if (t2==EOF) goto oops;
216
  t3=fgetc(prog_file);@+ if (t3==EOF) goto oops;
217
  cur_dat.h=(t0<<24)+(t1<<16)+(t2<<8)+t3;
218
  t0=fgetc(prog_file);@+ if (t0==EOF) goto oops;
219
  t1=fgetc(prog_file);@+ if (t1==EOF) goto oops;
220
  t2=fgetc(prog_file);@+ if (t2==EOF) goto oops;
221
  t3=fgetc(prog_file);@+ if (t3==EOF) goto oops;
222
  cur_dat.l=(t0<<24)+(t1<<16)+(t2<<8)+t3;
223
  return true;
224
oops: fprintf(stderr,"Premature end of file on %s!\n",prog_file_name);
225
@.Premature end of file...@>
226
  return false;
227
}
228
 
229
@ @=
230
while (1) {
231
  if (!undump_octa()) {
232
    fprintf(stderr,"Unexpected end of file on %s!\n",prog_file_name);
233
@.Unexpected end of file...@>
234
    break;
235
  }
236
  if (!(cur_dat.h || cur_dat.l)) break;
237
  if (bad_address) {
238
    fprintf(stderr,"Panic: Unsupported virtual address %08x%08x!\n",
239
@.Unsupported virtual address@>
240
                     cur_loc.h,cur_loc.l);
241
    exit(-5);
242
  }
243
  if (new_chunk) mem_write(cur_loc,cur_dat);
244
  else mem_hash[last_h].chunk[(cur_loc.l&0xffff)>>3]=cur_dat;
245
  cur_loc.l+=8;
246
  if ((cur_loc.l&0xfff8)!=0) new_chunk=false;
247
  else {
248
    new_chunk=true;
249
    if ((cur_loc.l&0xffff0000)==0) {
250
      bad_address=true; cur_loc.h=(cur_loc.h<<29)+1;
251
    }
252
  }
253
}
254
 
255
@ The primitive operating system assumed in simple programs of {\sl The
256
Art of Computer Programming\/} will set up text segment, data segment,
257
pool segment, and stack segment as in {\mc MMIX-SIM}. The runtime stack
258
will be initialized if we \.{UNSAVE} from the last location loaded
259
in the \.{.mmb} file.
260
 
261
@d rQ 16
262
 
263
@=
264
if (cur_loc.h!=3) {
265
  fprintf(stderr,"Panic: MMIX binary file didn't set up the stack!\n");
266
@.MMIX binary file...@>
267
  exit(-6);
268
}
269
inst_ptr.o=mem_read(incr(cur_loc,-8*14)); /* \.{Main} */
270
inst_ptr.p=NULL;
271
cur_loc.h=0x60000000;
272
g[255].o=incr(cur_loc,-8); /* place to \.{UNSAVE} */
273
cur_dat.l=0x90;
274
if (mem_read(cur_dat).h) inst_ptr.o=cur_dat; /* start at |0x90| if nonzero */
275
head->inst=(UNSAVE<<24)+255, tail--; /* prefetch a fabricated command */
276
head->loc=incr(inst_ptr.o,-4); /* in case the \.{UNSAVE} is interrupted */
277
g[rT].o.h=0x80000005, g[rTT].o.h=0x80000006;
278
cur_dat.h=(RESUME<<24)+1, cur_dat.l=0, cur_loc.h=5, cur_loc.l=0;
279
mem_write(cur_loc,cur_dat); /* the primitive trap handler */
280
cur_dat.l=cur_dat.h, cur_dat.h=(NEGI<<24)+(255<<16)+1;
281
cur_loc.h=6, cur_loc.l=8;
282
mem_write(cur_loc,cur_dat); /* the primitive dynamic trap handler */
283
cur_dat.h=(GET<<24)+rQ, cur_dat.l=(PUTI<<24)+(rQ<<16), cur_loc.l=0;
284
mem_write(cur_loc,cur_dat); /* more of the primitive dynamic trap handler */
285
cur_dat.h=0, cur_dat.l=7; /* generate a PTE with \.{rwx} permission */
286
cur_loc.h=4; /* beginning of skeleton page table */
287
mem_write(cur_loc,cur_dat); /* PTE for the text segment */
288
ITcache->set[0][0].tag=zero_octa;
289
ITcache->set[0][0].data[0]=cur_dat; /* prime the IT cache */
290
cur_dat.l=6; /* PTE with read and write permission only */
291
cur_dat.h=1, cur_loc.l=3<<13;
292
mem_write(cur_loc,cur_dat); /* PTE for the data segment */
293
cur_dat.h=2, cur_loc.l=6<<13;
294
mem_write(cur_loc,cur_dat); /* PTE for the pool segment */
295
cur_dat.h=3, cur_loc.l=9<<13;
296
mem_write(cur_loc,cur_dat); /* PTE for the stack segment */
297
g[rK].o=neg_one; /* enable all interrupts */
298
g[rV].o.h=0x369c2004;
299
page_bad=false, page_r=4<<(32-13), page_s=32, page_mask.l=0xffffffff;
300
page_b[1]=3, page_b[2]=6, page_b[3]=9, page_b[4]=12;
301
 
302
@* Interaction. When prompted for instructions, this simulator
303
@.mmmix>@>
304
understands the following terse commands:
305
 
306
\def\bull{\smallbreak\textindent{$\bullet$}}
307
\def\<#1>{$\langle\,$#1$\,\rangle$}
308
\bull\: Run for this many clock cycles.
309
 
310
\bull\.{@@}\: Set the instruction pointer
311
to this virtual address; successive instructions will be fetched from here.
312
 
313
\bull\.{b}\: Set the breakpoint
314
to this virtual address; simulation will pause when an instruction from the
315
breakpoint address enters the fetch buffer.
316
 
317
\bull\.v\: Set the desired level of diagnostic
318
output; each bit in the hexadecimal integer enables certain printouts
319
when the simulator is running. Bit \Hex1 shows instructions when issued,
320
deissued, or committed; \Hex2 shows the pipeline and locks after each cycle;
321
\Hex4 shows each coroutine activation; \Hex8 each coroutine scheduling;
322
\Hex{10} reports when reading from an uninitialized chunk of memory;
323
\Hex{20} asks for online input when reading from addresses $\ge2^{48}$;
324
\Hex{40} reports all I/O to memory address $\ge2^{48}$;
325
\Hex{80} shows details of branch prediction;
326
\Hex{100} displays full cache contents including blocks with invalid tags.
327
 
328
\bull\.-\: Deissue this many instructions.
329
 
330
\bull\.l\ or \.g\: Show current ``hot'' contents
331
of a local or global register.
332
 
333
\bull\.m\: Show current contents of a physical memory
334
address. (This value may not be up to date; newer values might appear
335
in the write buffer and/or in the caches.)
336
 
337
\bull\.f\: Insert a tetrabyte into the fetch buffer.
338
(Use with care!)
339
 
340
\bull\.i\: Set the interval counter rI to the given value; this will
341
trigger an interrupt after the specified number of cycles.
342
 
343
\bull\.{IT}, \.{DT}, \.I, \.D, or \.S: Show current contents of a cache.
344
 
345
\bull\.{D*} or \.{S*}: Show dirty blocks of a cache.
346
 
347
\bull\.p: Show current contents of the pipeline.
348
 
349
\bull\.s: Show current statistics on branch prediction and
350
speed of instruction issue.
351
 
352
\bull\.h: Help (show the possibilities for interaction).
353
 
354
\bull\.q: Quit.
355
 
356
@=
357
while (1) {
358
  printf("mmmix> ");@+fflush(stdout);
359
@.mmmix>@>
360
  fgets(buffer,BUF_SIZE,stdin);
361
  switch (buffer[0]) {
362
default: what_say:
363
  printf("Eh? Sorry, I don't understand. (Type h for help)\n");
364
  continue;
365
case 'q': case 'x': goto done;
366
  @@;
367
  }
368
}
369
done:@;
370
 
371
@ @=
372
case 'h': case '?': printf("The interactive commands are as follows:\n");
373
  printf("  to run for n cycles\n");
374
  printf(" @@ to take next instruction from location x\n");
375
  printf(" b to pause when location x is fetched\n");
376
  printf(" v to print specified diagnostics when running;\n");
377
  printf("    x=1[insts enter/leave pipe]+2[whole pipeline each cycle]+\n");
378
  printf("      4[coroutine activations]+8[coroutine scheduling]+\n");
379
  printf("      10[uninitialized read]+20[online I/O read]+\n");
380
  printf("      40[I/O read/write]+80[branch prediction details]+\n");
381
  printf("      100[invalid cache blocks displayed too]\n");
382
  printf(" - to deissue n instructions\n");
383
  printf(" l to print current value of local register n\n");
384
  printf(" g to print current value of global register n\n");
385
  printf(" m to print current value of memory address x\n");
386
  printf(" f to insert instruction x into the fetch buffer\n");
387
  printf(" i to initiate a timer interrupt after n cycles\n");
388
  printf(" IT, DT, I, D, or S to print current cache contents\n");
389
  printf(" D* or S* to print dirty blocks of a cache\n");
390
  printf(" p to print current pipeline contents\n");
391
  printf(" s to print current stats\n");
392
  printf(" h to print this message\n");
393
  printf(" q to exit\n");
394
  printf("(Here  is a decimal integer,  is hexadecimal.)\n");
395
  continue;
396
 
397
@ @=
398
case '0': case '1': case '2': case '3': case '4':
399
case '5': case '6': case '7': case '8': case '9':
400
  if (sscanf(buffer,"%d",&n)!=1) goto what_say;
401
  printf("Running %d at time %d",n,ticks.l);
402
  if (bp.h==(tetra)-1 && bp.l==(tetra)-1) printf("\n");
403
  else printf(" with breakpoint %08x%08x\n",bp.h,bp.l);
404
  MMIX_run(n,bp);@+continue;
405
case '@@': inst_ptr.o=read_hex(buffer+1);@+inst_ptr.p=NULL;@+continue;
406
case 'b': bp=read_hex(buffer+1);@+continue;
407
case 'v': verbose=read_hex(buffer+1).l;@+continue;
408
 
409
@ @=
410
int n,m; /* temporary integer */
411
octa bp={-1,-1}; /* breakpoint */
412
octa tmp; /* an octabyte of temporary interest */
413
static unsigned char d[BUF_SIZE];
414
 
415
@ Here's a simple program to read an octabyte in hexadecimal notation
416
from a buffer. It changes the buffer by storing a null character
417
after the input.
418
@^radix conversion@>
419
 
420
@=
421
octa read_hex @,@,@[ARGS((char *))@];@+@t}\6{@>
422
octa read_hex(p)
423
  char *p;
424
{
425
  register int j,k;
426
  octa val;
427
  val.h=val.l=0;
428
  for (j=0;;j++) {
429
    if (p[j]>='0' && p[j]<='9') d[j]=p[j]-'0';
430
    else if (p[j]>='a' && p[j]<='f') d[j]=p[j]-'a'+10;
431
    else if (p[j]>='A' && p[j]<='F') d[j]=p[j]-'A'+10;
432
    else break;
433
  }
434
  p[j]='\0';
435
  for (j--,k=0;k<=j;k++) {
436
    if (k>=8) val.h+=d[j-k]<<(4*k-32);
437
    else val.l+=d[j-k]<<(4*k);
438
  }
439
  return val;
440
}
441
 
442
@ @=
443
case '-':@+ if (sscanf(buffer+1,"%d",&n)!=1 || n<0) goto what_say;
444
  if (cool<=hot) m=hot-cool;@+else m=(hot-reorder_bot)+1+(reorder_top-cool);
445
  if (n>m) deissues=m;@+else deissues=n;
446
  continue;
447
case 'l':@+ if (sscanf(buffer+1,"%d",&n)!=1 || n<0) goto what_say;
448
  if (n>=lring_size) goto what_say;
449
  printf("  l[%d]=%08x%08x\n",n,l[n].o.h,l[n].o.l);@+continue;
450
case 'm': tmp=mem_read(read_hex(buffer+1));
451
  printf("  m[%s]=%08x%08x\n",buffer+1,tmp.h,tmp.l);@+continue;
452
 
453
@ The register stack pointers, rO and rS, are not kept up to date
454
in the |g| array. Therefore we have to deduce their values by
455
examining the pipeline.
456
 
457
@=
458
case 'g':@+ if (sscanf(buffer+1,"%d",&n)!=1 || n<0) goto what_say;
459
  if (n>=256) goto what_say;
460
  if (n==rO || n==rS) {
461
    if (hot==cool) /* pipeline empty */
462
      g[rO].o=sl3(cool_O), g[rS].o=sl3(cool_S);
463
    else g[rO].o=sl3(hot->cur_O), g[rS].o=sl3(hot->cur_S);
464
  }
465
  printf("  g[%d]=%08x%08x\n",n,g[n].o.h,g[n].o.l);
466
  continue;
467
 
468
@ @=
469
static octa sl3 @,@,@[ARGS((octa))@];@+@t}\6{@>
470
static octa sl3(y) /* shift left by 3 bits */
471
  octa y;
472
{
473
  register tetra yhl=y.h<<3, ylh=y.l>>29;
474
    y.h=yhl+ylh;@+ y.l<<=3;
475
  return y;
476
}
477
 
478
@ @=
479
case 'I': print_cache(buffer[1]=='T'? ITcache: Icache,false);@+continue;
480
case 'D': print_cache(buffer[1]=='T'? DTcache: Dcache,@/
481
       buffer[1]=='*');@+continue;
482
case 'S': print_cache(Scache,buffer[1]=='*');@+continue;
483
case 'p': print_pipe();@+print_locks();@+continue;
484
case 's': print_stats();@+continue;
485
case 'i':@+ if (sscanf(buffer+1,"%d",&n)==1) g[rI].o=incr(zero_octa,n);
486
  continue;
487
 
488
@ @=
489
case 'f': tmp=read_hex(buffer+1);
490
 {
491
   register fetch* new_tail;
492
   if (tail==fetch_bot) new_tail=fetch_top;
493
   else new_tail=tail-1;
494
   if (new_tail==head) printf("Sorry, the fetch buffer is full!\n");
495
   else {
496
     tail->loc=inst_ptr.o;
497
     tail->inst=tmp.l;
498
     tail->interrupt=0;
499
     tail->noted=false;
500
     tail=new_tail;
501
   }
502
   continue;
503
 }
504
 
505
@ A hidden case here, for me when debugging.
506
It essentially disables the translation caches, by mapping everything
507
to zero.
508
 
509
@=
510
case 'd':@+if (ticks.l)
511
   printf("Sorry: I disable ITcache and DTcache only at the beginning!\n");
512
 else {
513
   ITcache->set[0][0].tag=zero_octa;
514
   ITcache->set[0][0].data[0]=seven_octa;
515
   DTcache->set[0][0].tag=zero_octa;
516
   DTcache->set[0][0].data[0]=seven_octa;
517
   g[rK].o=neg_one;
518
   page_bad=false;
519
   page_mask=neg_one;
520
   inst_ptr.p=(specnode*)1;
521
 }@+continue;
522
 
523
@ And another case, for me when kludging. At the moment,
524
it simply lists the functional unit names.
525
 
526
But I might decide to put other stuff here when giving a demo.
527
 
528
@=
529
case 'k':@+ { register int j;
530
   for (j=0;j
531
     printf("unit %s %d\n",funit[j].name,funit[j].k);
532
 }
533
 continue;
534
 
535
@ @=
536
bool bad_address;
537
extern bool page_bad;
538
extern octa page_mask;
539
extern int page_r,page_s,page_b[5];
540
extern octa zero_octa;
541
extern octa neg_one;
542
octa seven_octa={0,7};
543
extern octa incr @,@,@[ARGS((octa y,int delta))@];
544
  /* unsigned $y+\delta$ ($\delta$ is signed) */
545
extern void mmix_io_init @,@,@[ARGS((void))@];
546
extern void MMIX_config @,@,@[ARGS((char*))@];
547
 
548
@* Index.

powered by: WebSVN 2.1.0

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