Initial commit of OpenSPARC T2 design and verification files.
[OpenSPARC-T2-DV] / tools / perl-5.8.0 / lib / site_perl / 5.8.0 / Verilog / CodeGen.pm
CommitLineData
86530b38
AT
1package Verilog::CodeGen;
2
3use vars qw( $VERSION );
4$VERSION='0.9.4';
5
6#################################################################################
7# #
8# Copyright (C) 2002,2003 Wim Vanderbauwhede. All rights reserved. #
9# This program is free software; you can redistribute it and/or modify it #
10# under the same terms as Perl itself. #
11# #
12#################################################################################
13
14#print STDOUT "//Package Verilog::CodeGen loaded\n";
15
16use sigtrap qw(die untrapped normal-signals
17 stack-trace any error-signals);
18
19use strict;
20
21################################################################################
22
23 @Verilog::CodeGen::ISA = qw(Exporter);
24 @Verilog::CodeGen::EXPORT =qw(
25 &make_module
26 &create_objtest_code
27 &create_code_template
28 );
29
30
31#Modify this to use different compiler/simulator/viewer
32my $compiler="/usr/bin/iverilog";
33my $simulator="/usr/bin/vvp";
34my $vcdviewer="/usr/local/bin/gtkwave";
35
36# to store the module code for all modules
37my %modules;
38
39# for print configuration
40my %printcfg=(_fh=>*STDOUT,_fn=>'');
41
42#------------------------------------------------------------------------------
43#object constructor
44 sub new {
45#next 2 lines are fine in general, but for me a fixed class is better (saves typing)
46#my $invocant=shift;
47#my $class=ref($invocant) || $invocant;
48my $class="Verilog::CodeGen";
49my @keysvals=@_;
50if ($keysvals[0] ne 'type'){unshift @keysvals,'type';unshift @_,'type'}
51my @keys=();
52my @args=();
53while (@keysvals) {
54push @keys, shift @keysvals;
55push @args, shift @keysvals;
56}
57
58#this is extremely redundant: the hash contains an array with its keys and an array with its values, so everything is there twice!!
59#but it's convenient
60
61my $self= { '_keys'=>[@keys],'_args'=>[@args],@_ };
62
63my $modulekey=join('_',@args);
64$modulekey=~s/\.//g;
65$self->{modulename}=$modulekey;
66
67my $sub=$self->{type};
68$sub='gen_'.$sub;
69SOFTREF: {
70no strict "refs";
71$self->{code}=&$sub($self);
72}
73#eval never works as it should for me
74#eval('$self->{code}= &'.$sub.'($self);');
75
76bless($self,$class);
77
78$modules{$modulekey}=$self->{code};
79
80return $self;
81 }
82
83#===============================================================================
84
85# this is a class method to set the print options
86sub output {
87my $invocant=shift;
88my $arg=$invocant;
89if($invocant=~/=HASH\(0x/) {
90 $arg=shift;
91 }
92 my $fh;
93 my $filename;
94if(!$arg || $arg!~/\n|\s/m) { # store the attributes
95
96 if(!$arg) {
97 $fh=*STDOUT;
98 } elsif (scalar($arg)!~/^\*/ ) {
99 $filename=$arg;
100 $fh=*_VER;
101 } else {
102 $fh=$arg;
103 }
104$printcfg{_fh}=$fh;
105 if($filename){
106$printcfg{_fn}=$filename;
107unlink $filename;
108}
109} else { # print the string
110my $fh=$printcfg{_fh}||*STDOUT;
111my $filename=$printcfg{_fn};
112
113 if($filename) {
114 open $fh,">>$filename";
115 }
116
117 print $fh $arg;
118
119 if($filename) {
120 close $fh;
121 }
122
123}
124return $arg;
125}
126#===============================================================================
127#this is a class method to print all currently used modules
128sub modules {
129my $fh=$printcfg{_fh}||*STDOUT;
130my $filename=$printcfg{_fn};
131
132 if($filename) {
133 open $fh,">>$filename";
134 }
135foreach my $key (keys %modules) {
136print $fh "
137//
138// $key
139//
140";
141 print $fh $modules{$key};
142print $fh "
143
144//==============================================================================
145
146";
147}
148 if($filename) {
149 close $fh;
150 }
151}
152
153#===============================================================================
154
155sub module {
156my $objref=shift;
157$objref->output($objref->{code});
158}
159
160#===============================================================================
161# this is what the enduser uses to print instances
162sub instance {
163my $objref=shift;
164
165my $suffix=shift ; # throw away 'suffix'
166
167if($suffix) {
168if($suffix eq 'suffix'){
169$suffix=shift;
170}
171} else {$suffix=''}
172
173# get module pins
174my $modpins=$objref->{pins};
175$modpins=~s/[)(]//g;
176$modpins=~s/\s+//g;
177my @modpins=split(',',$modpins);
178my %pinlist=();
179# assign to nets with same name as default
180foreach my $modpin (@modpins) {
181$pinlist{$modpin}='.'.$modpin.'('.$modpin.')';
182}
183
184# override defaults
185while (@_) {
186my $pin=shift;
187my $net=shift;
188$pinlist{$pin}='.'.$pin.'('.$net.')';
189}
190
191my @pinlist=();
192foreach my $key (keys %pinlist) {
193push @pinlist,$pinlist{$key};
194}
195
196my $pins;
197if(@pinlist>1) {
198$pins=join(',',@pinlist)
199} else {$pins=$pinlist[0]}
200my @args=@{$objref->{'_args'}};
201my $args=join('_',@args);
202$args=~s/\.//g;
203
204my $instlabel="x_${args}_$suffix";
205my $instline= "$args $instlabel ($pins);\n";
206# this is fancy and unnecessary, but anyway
207# all instances of a given object
208$objref->{_instances}->{$suffix}=$instline;
209$objref->{$suffix}=$instlabel;
210# we need: $objname->{$suffix} => $instlabel
211$objref->output($instline);
212}
213#===============================================================================
214# this is for use inside modules
215# no print, just return the string
216sub inst {
217my $objref=shift;
218
219my $suffix=shift ; # throw away 'suffix'
220if($suffix) {
221if($suffix eq 'suffix'){
222$suffix=shift;
223}
224}else {$suffix=''}
225# get module pins
226my $modpins=$objref->{pins};
227$modpins=~s/[)(]//g;
228$modpins=~s/\s+//g;
229my @modpins=split(',',$modpins);
230my %pinlist=();
231# assign to nets with same name as default
232foreach my $modpin (@modpins) {
233$pinlist{$modpin}='.'.$modpin.'('.$modpin.')';
234}
235
236# override defaults
237while (@_) {
238my $pin=shift;
239my $net=shift;
240$pinlist{$pin}='.'.$pin.'('.$net.')';
241}
242
243my @pinlist=();
244foreach my $key (keys %pinlist) {
245push @pinlist,$pinlist{$key};
246}
247
248my $pins;
249if(@pinlist>1) {
250$pins=join(',',@pinlist)
251} else {$pins=$pinlist[0]}
252my @args=@{$objref->{'_args'}};
253my $args=join('_',@args);
254$args=~s/\.//g;
255
256my $instlabel="x_${args}_$suffix";
257my $instline= "$args $instlabel ($pins);\n";
258# this is fancy and unnecessary, but anyway
259$objref->{_instances}->{$suffix}=$instline;
260$objref->{$suffix}=$instlabel;
261return $instline;
262
263}
264
265#===============================================================================
266
267sub search {
268my $objref=shift;
269my $pattern=shift;
270my $result='';
271my @code=split("\n",$objref->{code});
272foreach my $line (@code){
273 if($line=~/$pattern/){
274#print STDOUT "$line\n";
275$result.= "$line\n";
276}
277}
278return $result;
279}
280#===============================================================================
281
282sub find_inst { # returns an array ref if multiple instances!
283my $objref=shift;
284my $pattern=shift;
285my @result=();
286my @code=split("\n",$objref->{code});
287foreach my $line (@code){
288
289 if($line=~/$pattern.*$pattern/) {
290my $instlabel=$line;
291$instlabel=~s/^.*?\s+//;
292$instlabel=~s/\s.*$//;
293push @result, $instlabel;
294}
295}
296 if (@result>1){
297return [@result];
298} else {
299return $result[0];
300}
301
302
303}
304#===============================================================================
305# a class method to run the netlist through Icarus Verilog
306sub run {
307my $netlist=shift;
308if(scalar($netlist)=~/=HASH\(0x/){
309$netlist=shift;
310}
311
312if(!$netlist) {
313$netlist=$printcfg{_fn};
314}
315#$netlist=~/\.v$/ || $netlist.='.v';
316if ($netlist){
317system("$compiler -o${netlist}vp $netlist");
318system("$simulator ${netlist}vp");
319} else {print STDERR "The run() method only works if the netlist is printed to a file.\n"}
320}
321#===============================================================================
322# a class method to plot the results with GTKWave
323sub plot {
324my $netlist=shift;
325if(scalar($netlist)=~/=HASH\(0x/){
326$netlist=shift;
327}
328
329if(!$netlist) {
330$netlist=$printcfg{_fn};
331}
332#$netlist=~/\.v$/ || $netlist.='.v';
333if (-e "${netlist}cd"){
334# send output to /dev/null to avoid blocking the socket!
335system("$vcdviewer ${netlist}cd >& /dev/null &");
336
337} else {print STDERR "The plot() method only works if the netlist is printed to a file.\n"}
338}
339
340################################################################################
341
342#function to convert decimal to binary
343sub dec2bin {
344
345 my $dec=shift;
346 my $nbits=shift;
347 my @Q=();
348 for my $nr (0..$nbits-1) {
349 my $n=$nbits-1-$nr;
350 if(($dec-2**$n)<0) {
351#MSB=left unshift @Q,'0';
352 push @Q,'0';
353 }
354 else
355 {
356# unshift @Q, '1';
357 push @Q, '1';
358 $dec=$dec-2**$n;
359 }
360 }
361 return [@Q];
362 } # END of dec2bin
363
364
365#===============================================================================
366sub splitbus {
367# bus[n0:0] => a[n0a:0],b[n0b:0],...
368# busname, width
369# sum of all widths=width of original bus
370my @nets=@_;
371my $busname=shift @nets;
372my $buswidth=shift @nets;
373
374my $fullwidth=0;
375
376# an addition for the frequent case of equidistant split:
377if(@nets==1) {
378my $n=shift(@nets);
379foreach my $i (0..$n-1) {
380 $nets[2*$i+1]=$buswidth/$n;
381 $nets[2*$i]=$busname.$i;
382}
383 $fullwidth=$buswidth;
384} else {
385foreach my $i (0..@nets/2-1) {
386$fullwidth+=$nets[2*$i+1];
387}
388}
389my $code="//generated with splitbus() function\n";
390
391if($fullwidth>$buswidth){
392 $code="//sum of width exceeds original bus width\n";
393 return $code;
394}
395if($fullwidth<$buswidth){
396 $code="//sum of width is smaller than original bus width\n";
397}
398
399my $begin=$fullwidth;
400foreach my $i (0..@nets/2-1) {
401 my $width=$nets[2*$i+1];
402 my $name=$nets[2*$i];
403 foreach my $b (1 ..$width) {
404# my $i=$width-$b;
405 my $i=$b-1;
406# my $bb=$begin-$b;
407 my $bb=$buswidth-($begin-$b)-1;
408 $code .="buf xBUF$name$b ($name\[$i\],$busname\[$bb\]);\n";
409 }
410 $begin-=$width;
411}
412return $code;
413} # END of splitbus
414
415#===============================================================================
416sub combinebus {
417# bus[n0:0] => a[n0a:0],b[n0b:0],...
418# busname, width
419# sum of all widths=width of original bus
420my @nets=@_;
421my $busname=shift @nets;
422my $buswidth=shift @nets;
423my $fullwidth=0;
424
425# an addition for the frequent case of equidistant split:
426if(@nets==1) {
427my $n=shift(@nets);
428foreach my $i (0..$n-1) {
429 $nets[2*$i+1]=$buswidth/$n;
430 $nets[2*$i]=$busname.$i;
431}
432 $fullwidth=$buswidth;
433} else {
434foreach my $i (0..@nets/2-1) {
435$fullwidth+=$nets[2*$i+1];
436}
437}
438
439my $code="//generated with combinebus() function\n";
440
441if($fullwidth>$buswidth){
442 $code="//sum of width exceeds destination bus width\n";
443 return $code;
444}
445if($fullwidth<$buswidth){
446 $code="//sum of width is smaller than destination bus width\n";
447}
448
449my $begin=$fullwidth;
450foreach my $i (0..@nets/2-1) {
451 my $width=$nets[2*$i+1];
452 my $name=$nets[2*$i];
453 foreach my $b (1 ..$width) {
454# my $i=$width-$b;
455 my $i=$b-1;
456# my $bb=$begin-$b;
457my $bb=$buswidth-($begin-$b)-1;
458
459 $code .="buf xBUF$name$b ($busname\[$bb\],$name\[$i\]);\n";
460 }
461 $begin-=$width;
462}
463return $code;
464} # END of combinebus
465
466#===============================================================================
467sub make_module {
468my $skip=shift||'';
469my $design=shift||'Verilog';
470
471my $up='';
472if($design eq 'Verilog') {
473use Cwd;
474my $dir=cwd();
475$dir=~s/.*\///;
476#we assume that a chdir to the design dir has been done
477if($dir ne 'Objects') {$design=$dir; $up='../'}
478} else { $up='../' }
479
480my $moduledir='DeviceLibs';
481
482if($skip eq ''){
483$moduledir=($design ne 'Verilog')?'../..':'../';
484} else {
485my @v=<$up../*.v>;
486if(@v) {
487system("cp $up../*.v ../DeviceLibs");
488}
489}
490my $date=`date`;
491chomp $date;
492my $me=`whoami`;
493chomp $me;
494
495my $header="
496#
497# Generated using Verilog::CodeGen::make_module
498# from Objects/*.pl
499#
500# by $me on $date
501#
502";
503if (($moduledir!~/\.\./)&& (not -d "$moduledir")){mkdir "$moduledir", 0755;}
504open(OUT,">$moduledir/$design.pm"); # .. ,../.. or DeviceLibs
505my $modulepath=$INC{'Verilog/CodeGen.pm'};
506open(IN,"<$modulepath");
507while(<IN>) {
508s/Verilog::CodeGen/DeviceLibs::$design/;
509/\s+\&make_module/ && next;
510/\s+\&create_objtest_code/ && next;
511/\s+\&create_code_template/ && do {
512print OUT '
513 %modules
514 %printcfg
515 &new
516 &output
517 &instance
518 &inst
519 &module
520 &modules
521 &run
522 &plot
523 &dec2bin
524 &splitbus
525 &combinebus
526';
527next;
528};
529print OUT $_;
530if(/use\ strict;/){
531print OUT $header;
532}
533
534}
535close IN;
536
537my @objects=<*.pl>;
538
539foreach my $obj (@objects) {
540$obj=~/R_/ && next;
541 if($obj ne "$skip.pl"){
542open(IN,"<$obj");
543my $ok=0;
544print OUT "
545#===============================================================================
546# $obj
547";
548while(<IN>){
549 if(/^sub/){$ok=1}
550 if($ok==1){
551print OUT $_;
552}
553}
554close IN;
555}
556}
557close OUT;
558
559} # END of make_module()
560
561#===============================================================================
562sub create_code_template {
563my $objname=shift;
564my $design=shift||'Verilog';
565
566if($design eq 'Verilog') {
567use Cwd;
568my $dir=cwd();
569$dir=~s/.*\///;
570if($dir ne 'Objects') {$design=$dir; }
571}
572
573#-------------------------------------------------------------------------------
574my $templ=<<'ENDTEMPL';
575sub gen_code_template {
576
577#purpose
578
579#args: varname
580
581my $objref=shift;
582my $varname=$objref->{varname}||'DEFAULT';
583
584my $pins="(P1,...,Pn)";
585my $modname=$objref->{modulename};
586my $code = "
587module $modname $pins;
588";
589$code.='//module code';
590$code .="
591endmodule // $modname
592";
593
594$objref->{pins}=$pins;
595return $code;
596
597} # END of gen_code_template
598
599ENDTEMPL
600#-------------------------------------------------------------------------------
601
602 $templ=~s/code_template/$objname/gms;
603 $templ=~s/DesignName/$design/gms;
604
605open(OBJ,">$objname.pl");
606print OBJ $templ;
607close OBJ;
608
609} # END of create_code_template
610#===============================================================================
611sub create_objtest_code {
612my $objname=shift;
613my $design=shift||'Verilog';
614
615if($design eq 'Verilog') {
616use Cwd;
617my $dir=cwd();
618$dir=~s/.*\///;
619if($dir ne 'Objects') {$design=$dir; }
620}
621
622#-------------------------------------------------------------------------------
623my $templ=<<'ENDHEADER';
624#!/usr/bin/perl -w
625use strict;
626
627###############################################################################
628### This part makes it possible to test the object. ###
629my $obj='code_template';
630my $design='DesignName';
631my $up='';
632if(-d "../$design"){$up='../'}
633BEGIN {
634use Verilog::CodeGen;
635&make_module('code_template','DesignName');
636}
637use lib '.';
638use DeviceLibs::DesignName;
639
640my $test=new("$obj");
641my $code= $test->{code};
642
643print $code;
644($design eq 'Verilog') && ($design='');
645chdir("$up../../TestObj/$design");
646
647open (VER,">${obj}_default.v");
648print VER $code;
649close VER;
650
651package DeviceLibs::DesignName;
652
653###############################################################################
654### The actual object code starts here ###
655
656ENDHEADER
657#-------------------------------------------------------------------------------
658
659 $templ=~s/code_template/$objname/gms;
660 $templ=~s/DesignName/$design/gms;
661
662open(OBJ,"<$objname.pl");
663while(<OBJ>){
664$templ.=$_;
665}
666close OBJ;
667
668open(OBJ,">$objname.tb");
669print OBJ $templ;
670close OBJ;
671
672} # END of create_objtest_code
673#===============================================================================
674################################################################################
675=head1 NAME
676
677B<Verilog::CodeGen> - Verilog code generator
678
679=head1 SYNOPSIS
680
681 use Verilog::CodeGen;
682
683 mkdir 'DeviceLibs/Objects/YourDesign', 0755;
684 chdir 'DeviceLibs/Objects/YourDesign';
685
686 # if the directory YourDesign exists, the second argument can be omitted
687 # create YourModule.pl in YourDesign
688 &create_template_file('YourModule','YourDesign');
689
690 # create a device library for testing in DeviceLibs/Objects/DeviceLibs
691 &make_module('YourModule','YourDesign');
692
693 # create the final device library in DeviceLibs (once YourModule code is clean)
694 &make_module('','YourDesign');
695
696=head1 USAGE
697
698The most efficient way to use the code generator is using the GUI (L<scripts/gui.pl> in the distribution). Read the documentation in L<Verilog::CodeGen::Gui.pm>). Alternatively, you can use the scripts that the GUI uses to do the work (in the scripts/GUI folder). If you want to make your own, follow the L<SYNOPSIS>.
699
700Then edit the file YourModule.pl in the folder DeviceLibs/Objects/YourDesign.
701
702For example:
703
704 sub gen_YourModule {
705 my $objref=shift;
706 my $par=$objref->{parname}||1;
707
708 # Create Objects
709
710 my $submodule=new('SubModule',parname1=>$par);
711
712 # Instantiate
713
714 my $pins="(A,Z)";
715 my $modname=$objref->{modulename};
716 my $code = "
717 module $modname $pins;
718 input A;
719 output Z;
720 ";
721 $code.=$submodule->inst('suffix',P1=>'A');
722 $code .="
723 endmodule // $modname
724 ";
725 $objref->{pins}=$pins;
726 return $code;
727 } # END of gen_YourModule
728
729
730Then run C<perl YourModule.pl> to check if the code produces valid a Verilog module.
731
732If this is the case, add YourModule to the device library with C<&make_module()>
733
734Next, create a testbench test_YourModule.pl in a directory on the same level as DeviceLibs (TestObj if you use the GUI):
735
736 use lib '..';
737 use DeviceLibs::YourDesign;
738
739 my $device=new("S_buffer_demux",depth=>7,);
740
741 open (VER,">test_S_buffer_demux.v");
742
743 output(*VER);
744
745 modules();
746
747 print VER "
748 module test_S_buffer_demux;
749 wire A;
750 wire [7:0] S;
751 wire [6:0] Z;
752 wire D;
753
754 reg a;
755 reg [7:0] s;
756
757 assign A= a;
758 assign S= s;
759
760 reg _ck;
761 ";
762 $device->instance();
763 my $x=$device->{""};
764
765 print VER "
766 // clock generator
767 always begin: clock_wave
768 #10 _ck = 0;
769 #10 _ck = 1;
770
771 end
772
773 always @(posedge _ck)
774 begin
775 \$display(\" \%0d \%b \%b \",\$time,$x. Z,$x. D);
776 end
777
778 initial
779 begin
780 \$display(\"Time Z D\");
781 a<=1;
782 #25;
783 a<=0;
784 #25;
785 \$finish;
786 end
787 endmodule
788 ";
789 close VER;
790 run("test_S_buffer_demux.v");
791 #plot("test_S_buffer_demux.v");
792
793Execute the testbench script with C<perl test_YourModule.pl>.
794
795=head1 DESCRIPTION
796
797Provides an object-oriented environment to generate Verilog code for modules and testbenches. The Verilog::CodeGen module provides two functions, one to create a code template and another to create a Perl module which contains the device library. This module , DeviceLibs::YourDesign, provides the class methods and contains the objects for every Verilog module; the objects are created based on a fixed template.
798The purpose of this module is to allow the generation of customized Verilog modules. A Verilog module can have a large number of parameters like input and output bus width, buffer depth, signal delay etc. The code generator allows to create an object that will generate the Verilog module code for arbitraty values of the parameters.
799
800=head1 UTILITY SCRIPTS
801
802With the Perl module distribution come a number of utility scripts. The most important one is gui.pl, a GUI frontend for Verilog development using the code generator.
803
804=head1 MAIN METHODS
805
806=head2 B<new>(I<$object_name>[,%attributes]);
807
808Create a new Verilog module object. The object attributes are optional, the object should provide reasonable defaults.
809
810=head2 B<output([*filehandle_ref||$filename])>
811
812output() takes a reference to a filehandle or a filename as argument. These are stored in the global %printcfg. Without arguments, this defaults to STDOUT.
813If output() is called with as argument a string containing \n and/or \s, this string is printed on the current filehandle.
814
815=head2 B<modules>
816
817The code generator stores all submodules of a given module in the global %modules. Calling modules() prints the code for these modules on the current filehandle.
818
819=head2 B<instance([$instance_suffix,%connectivity])>
820
821The instance() method will print the code for the instantiation of the object on the current filehandle. An optional instance suffix can be specified (to distinguish between different instances of the same module), as well as the pin connectivity. If the connectivity for a pin is not specified, it defaults to the pin name.
822
823=head2 B<inst([$instance_suffix,%connectivity])>
824
825The inst() method will return the code for the instantiation of the object as a string. An optional instance suffix can be specified (to distinguish between different instances of the same module), as well as the pin connectivity. If the connectivity for a pin is not specified, it defaults to the pin name.
826
827=head2 B<run([$filename])>
828
829Run the netlist through the Icarus Verilog (http://www.icarus.com) open source verilog simulator. The filename is optional if it was specified with the output() method.
830
831=head2 B<plot([$filename])>
832
833Plot the result of the simulation with gtkwave. For this purpose, the \$dumpvar and \$dumpfile compiler directives must be present in the testbench code. The filename is optional if it was specified with the output() method.
834
835=head2 B<module('modulename')>
836
837This method can be used to print the code for a specified module on the current filehandle.
838
839=head2 B<search(/pattern/)>
840
841Search the verilog code for a given pattern.
842
843=head2 B<find_inst(/pattern/)>
844
845Find all instances matching /pattern/ in the netlist.
846
847=head1 MAIN ATTRIBUTES
848
849=head2 B<{$instance_suffix}>
850
851Returns the full instance name of the object.
852$x=$object->{$instance_suffix};
853
854=head1 TODO
855
856=over
857
858=item *
859
860Convert the utility scripts to functions to be called from Verilog::CodeGen.
861
862=item *
863
864Put the GUI scripts in a module Gui.pm.
865
866=item *
867
868Separate the code for testing purposes from the module object code.
869
870=back
871
872=head1 SEE ALSO
873
874Icarus Verilog L<http://icarus.com/eda/verilog/index.html>
875
876=head1 AUTHOR
877
878W. Vanderbauwhede B<wim@motherearth.org>.
879
880L<http://www.comms.eee.strath.ac.uk/~wim>
881
882=head1 COPYRIGHT
883
884Copyright (c) 2002 Wim Vanderbauwhede. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
885
886=cut