#!/usr/bin/perl
#/usr/local/bin/perl5
# plagen.pl
#
# Authors:   Dr. Doran Wilde - original static plagen in C
#            Floyd Millet    - dynamic schematic plagen in C
#            Scott Bowden    - dynamic layout plagen in C
#                              static and dynamic plagen in Perl
#            Emy LeFevre     - conversion from Mentor to Cadence
#
# Copyright (c) 1999 Brigham Young University. All rights reserved.
########################################################################    

#use diagnostics;  #uncomment for debugging
require 5;  # Perl 5 is required.

########################################################################
# Perl equivalents of #define, included into package main;
########################################################################
package main;
# definitions of 'where' 
use constant ORIGIN => 1;
use constant BL     => 1;
use constant BR     => 2;
use constant TR     => 3;
use constant TL     => 4;
use constant FIRST          => [1,1];             # first cell 
use constant TO_RIGHT       => [2,1];             # aligns last.BR to this.BL 
use constant TO_RIGHT_ABOVE => [3,4];             # aligns last.TR to this.TL 
use constant TO_LEFT        => [1,2];             # aligns last.BL to this.BR 
use constant TO_LEFT_ABOVE  => [4,3];             # aligns last.TL to this.TR 
use constant ABOVE          => [4,1];             # aligns last.TL to this.BL 
use constant ABOVE_RIGHT    => [3,2];             # aligns last.TR to this.BR 
use constant BELOW          => [1,4];             # aligns last.BL to this.TL 
use constant BELOW_RIGHT    => [2,3];             # aligns last.BR to this.TR 

# definitions of orientation
use constant ROT0   => 0;
use constant ROT90  => 1;
use constant ROT180 => 2;
use constant ROT270 => 3;
use constant MIRX   => 4;
use constant R180MY => 4;
use constant MXR90  => 5;
use constant R270MX => 5;
use constant R90MY  => 5;
use constant MIRY   => 6;
use constant R180MX => 6;
use constant MYR90  => 7;
use constant R90MX  => 7;
use constant R270MY => 7;

# define signal types 
use constant IN     => 1;
use constant OUT    => 2;
use constant POW    => 3;

package Position;

sub new {
    my $type = shift;
    my %params = @_;
    my $self = {};
    $self->{X}    = $params{X};
    $self->{Y}    = $params{Y};
    $self->{DX}   = $params{DX};
    $self->{DY}   = $params{DY};
    $self->{Name} = $params{Name};
    $self->{Cur_cell} = $params{Cur_cell};
    return bless $self, $type;
}

sub x        { my $self = shift; return $self->{X};        }
sub y        { my $self = shift; return $self->{Y};;       }
sub dx       { my $self = shift; return $self->{DX};       }
sub dy       { my $self = shift; return $self->{DY};       }
sub name     { my $self = shift; return $self->{Name};     }
sub cur_cell { my $self = shift; return $self->{Cur_cell}; }

package Cell;

sub new {
    my $type = shift;
    my %params = @_;
    my $self = {};
    $self->{X}    = $params{X};
    $self->{Y}    = $params{Y};
    $self->{Name} = $params{Name};
    $self->{Path} = $params{Path};
    return bless $self, $type;
}

sub print {
    my $self = shift;
    print $self->path," ",$self->name," ",$self->x," ",$self->y,"\n";
}

sub x {
    my $self = shift;
    if(defined $self->{X}){
        return $self->{X};
    } else {
        return "";
    }
}

sub y {
    my $self = shift;
    if(defined $self->{Y}){
	return $self->{Y};
    } else {
	return "";
    }
}

sub name {
    my $self = shift;
    if(defined $self->{Name}){
        return $self->{Name};
    } else {
	return "";
    }
}

sub path {
    my $self = shift;
    if(defined $self->{Path}){
        return $self->{Path};
    } else {
        return "";
    }
}

package main;

use constant GRID  => 0.5;  #grid resolution

########################################################################
# Start of subroutines for Cell generation
########################################################################

sub start_design(*){
    $dst = shift;
    $cur_cell = 0;
    $cur_x = 0;
    $cur_y = 0;
    $cur_dx = 0;
    $cur_dy = 0;
    $bl_x = 0;
    $bl_y = 0;
    $tr_x = 0;
    $tr_y = 0;

}

sub start_cell($){
    my($name) = shift;
    if(defined($open_cell)){
	die "? Cannot start '$name' because '".open_cell->name."' is not finished.\n";
    }
    $open_cell = $celhash{$name} = Cell->new( X => 0, Y => 0, Name => $name);
    $cur_cell = 0;
    $cur_x = 0;
    $cur_y = 0;
    $cur_dy = 0;
    $bl_x = 0;
    $bl_y = 0;
    $tr_x = 0;
    $tr_y = 0;
	$instance_num = 0;
	printf $dst 'deNewCellView("EE451" "%s" "layout" "maskLayout" nil)', $name;
	print $dst "\n";
	printf $dst 'ID=geGetWindowCellView()';
	print $dst "\n";

	printf SCH 'deNewCellView("EE451" "%s" "schematic" "schematic" nil)', $name;
	print SCH "\n";
	printf SCH 'ID2=geGetWindowCellView()';
	print SCH "\n";
	print SCH 'inID=dbOpenCellViewByType("basic" "ipin" "symbol" "" "r")';
	print SCH "\n";
	print SCH 'outID=dbOpenCellViewByType("basic" "opin" "symbol" "" "r")';
	print SCH "\n";
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-0.5 %d.5:-0.5) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;
	
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-1 %d.5:-1) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;
	
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-1.5 %d.5:-1.5) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;

	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-2 %d.5:-2) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;



}

sub reference_cell($$$$){
    my($path,$name,$x,$y) = @_;

    if(exists($celhash{$name})){
        die("? cell '$name' has already been defined.\n");
    } else {
        $celhash{$name} = Cell->new( X => $x, Y => $y, Name => $name, Path => $path);
    }

    $z = int($x/GRID + .5)*GRID - $x;

    if(abs($z) > 1.0e-12){
        printf "? cell '$name' - %.1f(x) not a multiple of grid %.1f.\n",$x,GRID;
    }
    $z = int($y/GRID + .5)*GRID - $y;

    if(abs($z) > 1.0e-12){
        printf "? cell '$name' - %.1f(y) not a multiple of grid %.1f.\n",$x,GRID;
    }
}

sub get_cell( $ ){
    my($name) = shift;
    if(!defined($open_cell)){
        print "? before getting cell '$name', start_design must be called.\n";
    }
    if(exists $celhash{$name}){
        $cur_cell = $celhash{$name};
    } else {
        print "? can't get cell '$name'.\n";
        $cur_cell = 0;
    }
}

sub draw_cell(@){
    my($rs,$r1,$r2,$o,$s);
    if(@_ == 3){
        ($rs,$o,$s) = @_;
        ($r1,$r2) = @$rs;
    } elsif(@_ == 4){
        ($r1,$r2,$o,$s) = @_;
    } else {
        print STDERR "Wrong number of arguments to draw_cell\n";
        exit(1);
    }
    do_draw_cell($r1,$r2,$o,$s);
}

sub do_draw_cell( $$$$ ){
    my($ref1,$ref2,$orientation,$select) = @_;

    my($x,$y,$x2,$y2,$x3,$y3,$x4,$y4,$flip);
    select($dst);

    if(!cur_cell){
        print STDERR "? a cell must be selected before it can be drawn.\n";
        return;
    }

    $x = $cur_x;
    $y = $cur_y;
    
    if(   $ref1 == main::BR){ $x += $cur_dx; }
    elsif($ref1 == main::TR){ $x += $cur_dx;
                              $y += $cur_dy; }
    elsif($ref1 == main::TL){ $y += $cur_dy; }

    # (x2, y2) is coordinate used in the adjustment for second ref point
    # (x3, y3) is coordinate used in the adjustment for rotation
    
    if(   $orientation == main::ROT0  ){ $x2 = $cur_cell->x;
                                         $y2 = $cur_cell->y;
                                         $x3 = 0;
                                         $y3 = 0;
                                         $flip = '"R0"';
				     }
    elsif($orientation == main::ROT90 ){ $x2 = $cur_cell->y;
                                         $y2 = $cur_cell->x;
                                         $x3 = $cur_cell->y;
                                         $y3 = 0;
                                         $flip = '"R90"';
				     }
    elsif($orientation == main::ROT180){ $x2 = $cur_cell->x;
                                         $y2 = $cur_cell->y;
                                         $x3 = $cur_cell->x;
                                         $y3 = $cur_cell->y;
                                         $flip = '"R180"';
				     }
    elsif($orientation == main::ROT270){ $x2 = $cur_cell->y;
                                         $y2 = $cur_cell->x;
                                         $x3 = 0;
                                         $y3 = $cur_cell->x;
                                         $flip = '"R270"';
				     }
    elsif($orientation == main::MIRX  ){ $x2 = $cur_cell->x;
                                         $y2 = $cur_cell->y;
                                         $x3 = $cur_cell->x;
                                         $y3 = 0;
                                         $flip = '"MY"';
				     }
    elsif($orientation == main::MXR90 ){ $x2 = $cur_cell->y;
                                         $y2 = $cur_cell->x;
                                         $x3 = $cur_cell->y;
                                         $y3 = $cur_cell->x;
                                         $flip = '"MYR90"';
				     }
    elsif($orientation == main::MIRY  ){ $x2 = $cur_cell->x;
                                         $y2 = $cur_cell->y;
                                         $x3 = 0;
                                         $y3 = $cur_cell->y;
                                         $flip = '"MX"';
				     }
    elsif($orientation == main::MYR90 ){ $x2 = $cur_cell->y;
                                         $y2 = $cur_cell->x;
                                         $x3 = 0;
                                         $y3 = 0;
                                         $flip = '"MXR90"';
				     }

    
    if($ref2 == main::ORIGIN){ $x += $x3;
                               $y += $y3;      }
    elsif($ref2 == main::BR ){ $x += $x3 - $x2;
                               $y += $y3;      }
    elsif($ref2 == main::TR ){ $x += $x3 - $x2;
                               $y += $y3 - $y2; }
    elsif($ref2 == main::TL ){ $x += $x3;
                               $y += $y3 - $y2; }


  	$instance_num++;
    printf('dbCreateInstByMasterName(ID "%s" "%s" "layout" "I%d" list(%s %s) %s)',
	   $cur_cell->path,$cur_cell->name,$instance_num,$x*.6,$y*.6,$flip);	
	   print "\n";

    $cur_x = $x - $x3;
    $cur_y = $y - $y3;
    $cur_dx = $x2;
    $cur_dy = $y2;

    # do bounding box 
    if ($bl_x > $cur_x){              $bl_x = $cur_x; }
    if ($bl_y > $cur_y){              $bl_y = $cur_y; }
    if ($tr_x < ($cur_x + $cur_dx) ){ $tr_x = $cur_x + $cur_dx; }
    if ($tr_y < ($cur_y + $cur_dy) ){ $tr_y = $cur_y + $cur_dy; }

    select(STDOUT);
}

sub add_port( $$$$$$$ ){
    my($name,$layer,$type,$x,$y,$dx,$dy) = @_;
    select($dst);

    $x += $cur_x;
    $y += $cur_y;

    printf('net = dbMakeNet(ID "%s")', $name);
    print "\n";
	printf('fig = dbCreateRect(ID list("%s" "drawing") list(%7.1f:%7.1f %7.1f:%7.1f))', $layer, $x*.6, $y*.6, ($x+$dx)*.6, ($y+$dy)*.6);
    print "\n";
	printf('trm = dbCreateTerm(net "%s" "%s")', $name, $type);
	print "\n";
    printf('pin = dbCreatePin(net fig "%s")', $name);
	print "\n";
	printf('pin~>accessDir = list("top" "bottom" "left" "right")');
	print "\n";
    printf('td = dbCreateTextDisplay( pin~>term pin~>term list("%s" "drawing") t %7.1f:%7.1f "centerCenter" "R0" "stick" 1.0 t nil t nil t "name" nil)', $layer, $x*.6+.9, $y*.6+.9); 
	print "\n";
    printf('td~>parent = fig'); 
	print "\n";
	print "\n";
	print "\n";
    select(STDOUT);
}

sub save_position( $ ){
    my($name) = shift @_;

    $pos_hash{$name} = Position->new( X  => $cur_x,  Y   => $cur_y,
                                      DX => $cur_dx, DY  => $cur_dy,
                                      Name => $name, Cur_cell => $cur_cell);    
}

sub restore_position( $ ){
    my($name) = shift @_;
    my($p);
    
    if($name eq "bounding_box"){
        $cur_x    = $bl_x;
	$cur_y    = $bl_y;
	$cur_dx   = $tr_x - $bl_x;
	$cur_dy   = $tr_y - $bl_y;
	$cur_cell = 0;
	return;
    }

    if(!exists $pos_hash{$name}){
        print "? can not find position '$name'.\n";
	exit(1);
    }
    
    $p = $pos_hash{$name};
    
    $cur_x =  $p->x;
    $cur_y =  $p->y;
    $cur_dx = $p->dx;
    $cur_dy = $p->dy;

    if ($p->cur_cell){ get_cell($p->cur_cell->name);
		   } else { $cur_cell = 0; }
}

sub end_cell {
    select($dst);

    printf 'dbSave(ID)';
	print "\n";
    printf 'dbClose(ID)';
	print "\n";

    $open_cell = 0;

    select(STDOUT);
}

sub end_design {
    if($open_cell){
        print STDERR "? ending design without closing last cell.\n";
	end_cell();
	exit(1);
    }
}


###################################################################
#  This is the end of the cell generation functions.
#  This is the start of the pla generation.
###################################################################
package Sig_record;

sub new {
    my $type = shift;
    my %params = @_;
    my $self = {};
    if(!exists($params{POS})){
        $self->{POS} = -1;
    } else {
        $self->{POS}   = $params{POS};
    }
    if(!exists($params{NUM})){
        $self->{NUM} = 0;
    } else {
        $self->{NUM}   = $params{NUM};
    }
    $self->{TYPE}  = $params{TYPE};
    $self->{BUF}   = $params{BUF};
    $self->{NAME}  = $params{NAME};
    $self->{LOAD0} = 0;
    $self->{LOAD1} = 0;
    $self->{"LOAD-"} = 0;
    return bless $self, $type;
}

sub pos   { my $self = shift; return $self->{POS};  }
sub num   { my $self = shift; return $self->{NUM};  }
sub type  { my $self = shift; return $self->{TYPE}; }
sub buf   { my $self = shift; return $self->{BUF}; }
sub name  { my $self = shift; return $self->{NAME}; }
sub load0 { my $self = shift; return $self->{LOAD0}; }
sub load1 { my $self = shift; return $self->{LOAD1}; }

package main;

# argument parsing
#
$dynamic = 0;
$kind = "static";
if($ARGV[0] =~m/^-(dynamic|dyn|d)$/){
    shift;
    $dynamic = 1;
    $kind = "dyn";
    print "Generating dynamic PLA\n";
} else {
    print "Generating static PLA\n";
}

sub croak {
    print STDERR @_;
    exit(1);
}

# error message if not exactly one filename
if(@ARGV != 1){
    print "Error... \n";
    print "Usage: plagen [-d|-dyn] name\n\n";
    print "The following files are opened:\n";
    print "\tname.pla\t-- The pla code file\n";
    print "\tname.ctl\t-- The pla layout control file\n\n";
    print "The following files are created:\n";
    print "\tname.ic \t-- The pla layout script\n";
    print "\tname.da \t-- The pla schematic script\n";
    exit(1);
}

$basepath = $ARGV[0];

# remove trailing period (often left from filename completion)
#if($basepath =~s/\.$//){
#    print "? Warning: removed trailing period from pla name...\n";
#    # What about if this is not a desired operation ???
#};  

$srcpath = $basepath.".pla";
$ctlpath = $basepath.".ctl";

########################################################################
# Initialization
########################################################################
$num_inputs = 0;
$num_outputs = 0;
$num_inp_spares = 0;
$num_out_spares = 0;
$num_sigs = 0;
$pla_terms = 0;
$in_clock = "";
$out_clock = "";

########################################################################
# read in first few lines of pla file 
########################################################################
open(SRC,$srcpath) or croak ("? cannot open file : $srcpath\n");

$pla_name = $ARGV[0];
print "Reading in $srcpath ...\n";
while(<SRC>){
    chomp;

#      if(/^\s*\.name\s+/){
#          $pla_name = $';
#      }

    if(/^\s*\.i\s+/){  # number of inputs
        $ni = $';
    }
    if(/^\s*\.o\s+/){  # number of outputs
        $no = $';
    }
    if(/^\s*\.p\s+/){  # number of minterms
        $nc = $';
    }
}

#probably need to check here that everything is defined

print "$pla_name -- $ni inputs.  $no outputs.  $nc terms.\n";
close SRC or croak ("? cannot close file : $srcpath\n");

########################################################################
# read in control file 
########################################################################
open(CTL,$ctlpath) or croak("? cannot open file : $ctlpath\n");

print "Reading in $ctlpath ...\n";

$ctl_line = "";
while(<CTL>){
    chomp;
    if(/\s*%INPUTS\s*(\d+)/){
        $num_inputs = $1;
    } elsif (/^\s*%OUTPUTS\s+(\d+)/){
        $num_outputs = $1;
    } elsif (/^\s*\%TERMS\s+(\d+)/){
        $pla_terms = $1;
    } elsif (/^\s*%IN_CLOCK\s+/){
        #$in_clock = uc($');
        $in_clock = $';
        $in_clockx = "n".$in_clock;
    } elsif (/^\s*%OUT_CLOCK\s+/){
        #$out_clock = uc($');
        $out_clock = $';
        #$out_clockx = $out_clock."x";
        $out_clockx = "n".$out_clock;
    } elsif (m(^(?!\s*%))) {
        $ctl_line .= "$_ ";
    }
}

if (!($nc == $pla_terms))
{
    croak ("? %TERMS ($pla_terms) specified in .ctl file is not equal to .p ($nc) line of .pla file\n");
}

$ctl_line =~ s/\// \/ /gs;
($sourceline,$layoutline) = split /\s*;\s*/, $ctl_line;

@sourceline = split /\s+/, $sourceline;
@layoutline = split /\s+/, $layoutline;

close CTL or croak("? cannot close file : $ctlpath\n");

###########################################################################
# Find signal names, whether they are input or output and whether they are
# clocked or not clocked 
###########################################################################

# find signal names from source(definition) list
$cur_type = 'INPUT';

$i = $o = 0;
foreach(@sourceline){
    if(/^\/$/){          # if find a '/', switch to outputs
        if($cur_type eq 'OUTPUT'){
            croak("? In source list : Too many '/'\n");
        }
        $cur_type = 'OUTPUT';
    } else {       # until end of line
        if(/^SPARE$/){
            croak "? SPARE cannot be in definition list\n" ;
        }
        if($cur_type eq 'INPUT'){
            $inp_sigs{$_} = 0;
            $isigs[$i++] = $_;
        } else {
            $out_sigs{$_} = 0;
            $osigs[$o++] = $_;
        }
    }
}

$cur_type = 'INPUT';
$cur_buf  = 'CLOCK';

# parse and store layout from ctl file
foreach (@layoutline){
    $rec = 0;
    if(/^\/$/){          # if find a '/', switch to outputs
        if($cur_type eq 'OUTPUT'){
            croak("? In layout list : Too many '/'\n");
        }
        while(($ni + $num_inp_spares) < $num_inputs){
            $num_inp_spares++;
            $rec = Sig_record->new( NAME => 'SPARE', 
                                    BUF  => $cur_buf,
                                    TYPE => 'INP_SPARE',
                                    POS  => $num_sigs++);
            push @sig_array, $rec;
            push @spares, $rec;
        }
        $cur_type = 'OUTPUT';
        $cur_buf  = 'CLOCK';
    } elsif(/^@/){       # if find a '@', set clock mode
        if(/\@clock/i){
            $cur_buf = 'CLOCK';
        } elsif(/\@noclock/i){
            $cur_buf = 'NOCLOCK';
        } else {
            croak ("!! Expecting \@clock or \@noclock, found $_\n");
        }
    } elsif(/^SPARE$/){
        if($cur_type eq 'INPUT'){
            $num_inp_spares++;
            $rec = Sig_record->new( NAME => 'SPARE', 
                                    BUF  => $cur_buf,
                                    TYPE => 'INP_SPARE',
                                    POS  => $num_sigs++);
            push @sig_array, $rec;
        } else {
            $num_out_spares++;
            $rec = Sig_record->new( NAME => 'SPARE', 
                                    BUF  => $cur_buf,
                                    TYPE => 'OUT_SPARE',
                                    POS  => $num_sigs++);
            push @sig_array, $rec;
        }
        push @spares, $rec;
    } else {      # while not at end of line
        if( $cur_type eq 'INPUT'){
            if( exists($inp_sigs{$_}) ){
                $inp_sigs{$_}++;
            } else {
                croak "? name '$_' is not in definition list.\n";
            }
        } elsif($cur_type eq 'OUTPUT'){
            if( exists($out_sigs{$_}) ){
                $out_sigs{$_}++;
            } else {
                croak "? name '$_' is not in definition list.\n";
            }
        }
        $rec = Sig_record->new( NAME => $_, 
                                BUF  => $cur_buf,
                                TYPE => $cur_type,
                                POS  => $num_sigs++);
        push @sig_array, $rec;
    }

    if($rec && defined($rec->name)){ # if a record defined and name exists
        # then substitute into schematic arrays the record in the place 
        # of the signal name.
        grep { if($_ eq $rec->name){ $_ = $rec } } @osigs;
        grep { if($_ eq $rec->name){ $_ = $rec } } @isigs;
    }
}
while(($no + $num_out_spares) < $num_outputs){
    $num_out_spares++;
    $rec = Sig_record->new( NAME => 'SPARE', 
                            BUF  => $cur_buf,
                            TYPE => 'OUT_SPARE',
                            POS  => $num_sigs++);
    push @sig_array, $rec;
    push @spares, $rec;
}

########################################################################
# This section does some basic error checking
########################################################################
foreach (keys %out_sigs){
    $count = $out_sigs{$_};
    if($count == 0 || !defined($count)){
        croak("? name '$_' is not in layout list.\n");
    } elsif($count >= 2){
        croak("? name '$_' is in layout list twice.\n");
    }
}

foreach (keys %inp_sigs){
    $count = $inp_sigs{$_};
    if($count == 0){
        croak("? name '$_' is not in layout list.\n");
    } elsif($count >= 2){
        croak("? name '$_' is in layout list twice.\n");
    }
}

if(($ni + $num_inp_spares) != $num_inputs){
    croak("? Number of inputs in pla file ($ni) does not match\n",
          "  number of inputs in ctl file ($num_inputs) - ",
          "number of spares ($num_inp_spares).\n");
}

if(($no + $num_out_spares) != $num_outputs){
    croak("? Number of outputs in pla file ($no) does not match\n",
          "  number of outputs in ctl file ($num_outputs) - ",
          "number of spares ($num_out_spares).\n");
}

if($ni != keys(%inp_sigs)){
    croak("? Number of output signals in source list does not match pla file.\n");
}
if($no != keys(%out_sigs)){
    croak("? Number of output signals in source list does not match pla file.\n");
}

########################################################################
# Open destination files
########################################################################
$dstpath = "$basepath\_l.il";       # layout script
$schpath = "$basepath\_s.il";       # schematic script
$verpath = "$basepath.v";      # cadence verilog file

open(DST,">$dstpath") or croak ("? cannot open file : $dstpath\n");
open(SCH,">$schpath") or croak ("? cannot open file : $schpath\n");
open(VER,">$verpath") or croak ("? cannot open file : $verpath\n");

# print SCH "";  # not needed

########################################################################
# Start pla generation
########################################################################
start_design(DST);
    #               lib         name       width    height
    reference_cell('PLA',"and0",      24.0,  12.5);
    reference_cell('PLA',"and1",      24.0,  12.5);
    reference_cell('PLA',"andx",      24.0,  12.5);
    reference_cell('PLA',"or0",       24.0,  12.5);
    reference_cell('PLA',"or1",       24.0,  12.5);
    reference_cell('PLA',"andpu",     29.5,  12.5);
    reference_cell('PLA',"orpu",      24.0,  12.5);
    reference_cell('PLA',"andtop",    24.0,  29.0);
    reference_cell('PLA',"putop",     29.5,  29.0);
    reference_cell('PLA',"orend",      7.0,  12.5);
    reference_cell('PLA',"ortop",      7.0,  29.0);
    reference_cell('PLA',"inbuf1",    24.0, 221.5);
    reference_cell('PLA',"inbuf2",    24.0, 221.5);
    reference_cell('PLA',"inbuf3",    24.0, 221.5);
    reference_cell('PLA',"inbuf4",    24.0, 221.5);
    reference_cell('PLA',"inbufx",    24.0, 221.5);
    reference_cell('PLA',"outbuf1",   24.0, 237.0);
    reference_cell('PLA',"outbuf2",   24.0, 237.0);
    reference_cell('PLA',"outbuf3",   24.0, 237.0);
    reference_cell('PLA',"outbuf4",   24.0, 237.0);
    reference_cell('PLA',"outbufx",   24.0, 237.0);
    reference_cell('PLA',"ioend1",    31.5, 221.5);
    reference_cell('PLA',"ioend2",     7.0, 237.0);
$pla_inputs  = $num_inputs;
$pla_outputs = $num_outputs;
$i = (($nc+1) & 0xfffffe);
if($pla_terms < $i){
    $pla_terms = $i;
}

# draw the skeleton of the array
start_cell($pla_name);
# --------------------- left side -------------------------
get_cell("ioend1");
draw_cell(BELOW, ROT0, 0);

add_port("gnd!",      "metal2", "input", 0, 184  , 3, 12);
add_port("vdd!",      "metal2", "input", 0, 115.5, 3, 12);
add_port($out_clock, "metal2", "input", 0, 108.5, 3,  3);
add_port($out_clockx,"metal2", "input", 0, 100.5, 3,  3);
add_port($in_clockx, "metal2", "input", 0,  62.5, 3,  3);
add_port($in_clock,  "metal2", "input", 0,  54.5, 3,  3);
# clk and and-plane pullups

get_cell("andpu");
foreach $i (0..($pla_terms/2) - 1) {
    draw_cell(ABOVE, MIRY, 0);
    save_position("bottomleft") if($i == 0);
    draw_cell(ABOVE, ROT0, 0);
}

get_cell("putop");
draw_cell(ABOVE, ROT0, 0);
save_position("topleft"); 
restore_position("bottomleft");

# ---------------- schematic ------------------- #
# draw AND plane pullups and minterms            #
# ---------------------------------------------- #

        printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "andpu" "symbol" "I%d" list(-0.5 1) "R0")',
	   ++$instance_num);	
	   print SCH "\n";

	for($t=1; $t<$pla_terms; $t++){
    printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "andpu" "symbol" "I%d" list(-0.5 %d) "R0")',
	   ++$instance_num,1+$t);	
	   print SCH "\n";
}
# These are the clock inputs for the plaPULLUPs


   	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:1 %d.5:1) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-1;
	for($t=1; $t<$pla_terms; $t++){
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:%d %d.5:%d) 0.0625
	0.0625
	0.0)\n",1+$t,2*$pla_inputs+$pla_outputs-1,1+$t;
   
}


#-----------------------------------------------#
# name the minterms                             #
#-----------------------------------------------#
#--------------------- bottom -------------------------#
# input buffers 
foreach $i (0..$pla_inputs-1){   
    $sig = $sig_array[$i];
    if ($sig->type eq 'INP_SPARE'){
        get_cell("inbufx");
    } elsif ($sig->buf eq 'CLOCK'){ 
        if ($i&1){ get_cell("inbuf1"); }
        else {     get_cell("inbuf2"); }
    } else {   
        if ($i&1){ get_cell("inbuf3"); }
        else {     get_cell("inbuf4"); }
    }
    if ($i==0) { draw_cell(BR,TL,ROT0,0); save_position("minterm"); }
    else { draw_cell(TO_RIGHT_ABOVE, ROT0, 0); }
    if($sig->type ne 'INP_SPARE'){
        add_port($sig->name,"metal1", "input" , 1.5, 0, 3, 3);
    }
}

# ---------------- Verilog ----------------------#
# part 1/3 : declarations                        #
#------------------------------------------------#

$" = ',';  # interpolation separator

print VER "module $pla_name($in_clock, $in_clockx, $out_clock, $out_clockx, "; 
foreach $sig (@isigs){
    printf VER "%s, ", $sig->name;
}
foreach $sig (@osigs){
    printf VER "%s", $sig->name;
       print VER ", "; 
}
foreach $sig (@osigs){
    printf VER "n%s", $sig->name;
    if ($sig ne $osigs[$#osigs]) { print VER ", "; }
}
print VER ");\n";
print VER "\n//list all clocks\n   input $in_clock, $in_clockx, $out_clock, $out_clockx;\n";
printf VER "\n//list all inputs\n   input ";
foreach $sig (@isigs){
   printf VER "%s", $sig->name;
   if ($sig ne $isigs[$#isigs]) {
      print VER ", ";
   }
}
print VER ";\n";
printf VER "\n//list all outputs\n   output ";
foreach $sig (@osigs){
   printf VER "%s", $sig->name;
       print VER ", ";
  }
foreach $sig (@osigs){
   printf VER "n%s", $sig->name;
   if ($sig ne $osigs[$#osigs]) {
      print VER ", ";
   } 
}
print VER ";\n\n";
print VER "//regs to hold clocked inputs\n";
foreach $sig (@isigs){
    if($sig->buf eq 'CLOCK'){
	printf VER "   reg ";
	printf VER "t%s", $sig->name;
    print VER ";\n";
    }
}
print VER "//regs to hold outputs\n   reg ";
foreach $sig (@osigs){
    	printf VER "%s", $sig->name;
	printf VER ", n%s", $sig->name; 
    if($sig->buf eq 'CLOCK'){
	printf VER ", t%s", $sig->name;
	}
	if ($sig ne $osigs[$#osigs]) {
	    print VER ", ";
	}
}
print VER ";\n\n";
print VER "//the and-plane part of minterms\n";
print VER "   reg [1:$num_inputs] ands[1:$nc];\n";
print VER "\n//b is a temp var, ors are the or-plane part of minterms\n";
print VER "   reg [1:$nc] b,ors[1:$num_outputs];\n";
print VER "\n    initial begin\n";
foreach $sig (@isigs){
    if($sig->buf eq 'CLOCK'){
	printf VER "        t%s = 1'b0;\n", $sig->name;
    }
}
foreach $sig (@osigs){
    if($sig->buf eq 'CLOCK'){
	printf VER "        %s = 1'b0;\n", $sig->name;
	printf VER "        n%s = 1'b0;\n", $sig->name;
	}
}

print VER "\n", '        $async$and$plane(ands,{';    # '...' = no interpolation, please
foreach $sig (@isigs){
    if($sig->buf eq 'CLOCK'){
	print VER "t"
	}
    printf VER "%s", $sig->name;
    if ($sig ne $isigs[$#isigs]) {
	print VER ", ";
    }
}
print VER "},{";
foreach $i (1..$nc) {
    print VER "b[$i]";
        if ($i<$nc) {
	print VER ",";
    }
}
print VER "});\n";
#-----------------------------------------------#
# End Verilog                                   #
#-----------------------------------------------#

#----------------- schematic -------------------#
# draw input buffers and pins                   #
#-----------------------------------------------#
foreach $sig (@isigs){
    #---------------- schematic -------------------#
    # draw an input buffer                         #
    #----------------------------------------------#
    if($sig->buf eq 'CLOCK'){
	    printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "inbuf1" "symbol" "I%d" list(%d 0) "R0")',
	   ++$instance_num,2*$sig->pos);	
	   print SCH "\n";
    } elsif ($sig->buf eq 'NOCLOCK'){
	
    printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "inbuf4" "symbol" "I%d" list(%d 0) "R0")',
	   ++$instance_num,2*$sig->pos);	
	   print SCH "\n";
}
        printf SCH ("schCreatePin(ID2 inID \"%s\" \"input\" nil %d.5:-2.75 \"R90\")",$sig->name, 2*$sig->pos); 
	print SCH "\n";
}

#----------------- schematic -------------------#
# add input wires into the array                #
#-----------------------------------------------#


    	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(0:0 0:%d.5) 0.0625
	0.0625 0.0)\n",$pla_terms;
	for($t=1; $t<$pla_inputs*2; $t++){
	if($t%2==0)
	{
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(%d:%d %d:%d.5) 0.0625
	0.0625
	0.0)\n",$t,0,$t,$pla_terms;}
}	
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(1:0 1:%d.5) 0.0625
	0.0625 0.0)\n",$pla_terms;

    for($t=1; $t<$pla_inputs*2; $t++){
	if($t%2==0){
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(%d:%d %d:%d.5) 0.0625
	0.0625
	0.0)\n",1+$t,0,1+$t,$pla_terms;
	}
}


#----------------------------------------------#
# End Schematic                                #
#----------------------------------------------#

#---------------- layout ----------------------#
# input buffers                                #
#----------------------------------------------#
foreach $i (0..$pla_outputs-1){   
    $sig = $sig_array[$i+$pla_inputs];
    if ($sig->type eq 'OUT_SPARE'){
	get_cell("outbufx");
    } elsif ($sig->buf eq 'CLOCK'){ 
	if (($i+$pla_inputs)&1){ get_cell("outbuf1"); }
	else {     get_cell("outbuf2"); }
    } else {   
	if (($i+$pla_inputs)&1){ get_cell("outbuf3"); }
	else {     get_cell("outbuf4"); }
    }
    draw_cell(TO_RIGHT_ABOVE, ROT0, 0);
    if($sig->type ne 'OUT_SPARE'){
	add_port($sig->name,    "metal1", "output" ,  1.5, 0, 3, 3);
	add_port("n".$sig->name,"metal1", "output" , 19.5, 0, 3, 3);
    }
}

#---------------- schematic --------------------
# add output output buffers, pins and names     
#-----------------------------------------------

foreach $sig (@osigs){
	$instance_num++;
    #----------------- schematic -------------------
    # draw an output buffer                         
    #-----------------------------------------------
    if($sig->buf eq 'CLOCK'){
		            printf SCH "dbCreateInstByMasterName(ID2 \"PLA\" \"outbuf1\" \"symbol\" \"I%d\" list(%d 0) \"R0\")\n",++$instance_num, $pla_inputs + $sig->pos;
	    } elsif($sig->buf eq 'NOCLOCK'){
		            printf SCH "dbCreateInstByMasterName(ID2 \"PLA\" \"outbuf3\" \"symbol\" \"I%d\" list(%d 0) \"R0\"\)\n",++$instance_num, $pla_inputs + $sig->pos;
    }
	    #----------------- schematic -------------------
    # false output
    	    printf SCH ("schCreatePin(ID2 outID \"n%s\" \"output\" nil %d.75:-2.75 \"R270\")",$sig->name, $pla_inputs+$sig->pos -1); 
	print SCH "\n";
             
    #---------------- schematic -------------------
    # true output 
    	    printf SCH ("schCreatePin(ID2 outID \"%s\" \"output\" nil %d.25:-2.75 \"R270\")",$sig->name, $pla_inputs+$sig->pos); 
	print SCH "\n";
             }
#----------------- schematic -------------------
# add delayed clock                             
#-----------------------------------------------
  

# ---------------- schematic -------------------
# add OR plane pullups and output wires         
#-----------------------------------------------
    
    printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "andpu" "symbol" "I%d" list(%d %d.5) "R270")',
	   ++$instance_num,2*$pla_inputs, $pla_terms);	
	   print SCH "\n";

	for($t=1; $t<$pla_outputs; $t++){
    printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "andpu" "symbol" "I%d" list(%d %d.5) "R270")',
	   ++$instance_num,2*$pla_inputs+$t,$pla_terms);	
	   print SCH "\n";
	}
   	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(%d:0 %d:%d.5) 0.0625
	0.0625 0.0)\n",2*$pla_inputs, 2*$pla_inputs, $pla_terms;
    
	for($t=1; $t<$pla_outputs; $t++){
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(%d:%d %d:%d.5) 0.0625
	0.0625
	0.0)\n",2*$pla_inputs+$t,0, 2*$pla_inputs+$t, $pla_terms;
}	

#--------------- layout ----------------#
# right side                            #
#---------------------------------------#
get_cell("ioend2"); 
draw_cell(TO_RIGHT_ABOVE, ROT0, 0);
get_cell("orend");
foreach (1..($pla_terms/2)){   
    draw_cell(ABOVE, MIRY, 0); draw_cell(ABOVE, ROT0, 0);
}

#--------------- layout ----------------#
# top                                   #
#---------------------------------------#
restore_position("topleft");


# and-plane top
for (1..$pla_inputs){
    get_cell("andtop");	
    draw_cell(TO_RIGHT, ROT0, 0);
}

# and-or plane interface 

# or-plane top
for (1..$pla_outputs ){
    get_cell("orpu");	
    draw_cell(TO_RIGHT, ROT0, 1);
}
get_cell("ortop"); 
draw_cell(TO_RIGHT, ROT0, 0);

#--------------------------------------------#
# BEGIN TO PROCESS MINTERMS                  #
#--------------------------------------------#
# -------- (re)read source (.pla file) ---------
open(SRC,$srcpath) or croak ("? cannot open file : $srcpath\n");

print "Reading in $srcpath ...\n";

$m = 0;
$end_of_pla_data = 0;
while($m < $pla_terms){
    @m0 = ();

    if ($end_of_pla_data == 0){
	if (!($_ = <SRC>)) {
	    print "unexpected end of file, expected a .e\n";
	}
	elsif (/^\s*\.e/) {
	    $end_of_pla_data = 1;
	    if($. < 6){
		print STDERR "WARNING: .e on line number $.. Are you sure that\n";
		print STDERR "the .e is after all valid pla data?\n";
	    }
	    next;
	}
	elsif ( (/^\s*$/) || (/^\s*\./) ){ #blank lines or .*
	    next;
	}
	
    }
    else {
	if ($m < $nc ) {
	    print "Error: not as many minterms defined as specified by .pla file\n";
	}
	foreach(1..$pla_inputs){
	    push @m0, '-';
	}
	foreach(1..$pla_outputs){
	    push @m0, '0';
	}
	draw_minterm_row($m,@m0);
	$m++;
	next;
    }

    #next if(/^\s*$/ || /^\s*\./);  # skip blank lines and lines that start with .

    
    chomp;         # remove newline

    s/\s//gs;      # remove spaces and tabs;
    s/\(.*?\)//gs; # remove comments 

    # a slash is optional
    s*/**g;        # remove first /

    if(m%/%){
	croak("? Too many slashes\n");
    }

    #-------------------- Verilog --------------------#
    # Part 2/3                                        #
    #-------------------------------------------------#
    $minterm = $_;
    chomp $minterm;                 # remove LF from string
    $minterm =~ s!-!?!g;            # change -'s to ?'s
    @minterm = split(//, $minterm); # break string into characters
    $and = "";                      # clear and string
    for ($i=0;$i<=$#minterm;$i++) { # for each character in minterm
      $mi = $minterm[$i];           # mi is i-th character
      if ($i<$ni) {                 # mi is an AND-plane character
	$and = $and . $mi;          # so append it to string and
      }
      else {                        # else mi is an OR-plane character
	push @ors, $mi;             # push character onto char array @ors
      }
    }
    printf VER "        ands[%d] = ${ni}'b$and;\n", $m + 1;
    #-------------------------------------------------#
    # End Verilog Section                             #
    #-------------------------------------------------#

    $input_load  = 0;
    $output_load = 0;
    
    @pla_line = split //;  #split into characters

    $ji = 0;
    foreach $sig (@isigs){
	if($sig->pos != -1){ 
	    $input_load++;
	    $l = $m0[$sig->pos] = $pla_line[$ji++];
	    $sig->{"LOAD$l"}++;
	} else { croak("? Input $ji not mapped in minterm $m."); }
    }
    $jo = 0;
    foreach $sig (@osigs){
	if($sig->pos != -1){ 
	    $output_load++;
	    $l = $m0[$sig->pos] = $pla_line[$ji+$jo++];
	    $sig->{"LOAD$l"}++;
	} else { croak("? Output $ji not mapped in minterm $m."); }
    }
    foreach $sig (@spares){
	if($sig->pos != -1){ 
	    $output_load++;
	    if($sig->type eq 'INP_SPARE'){
		$l = $m0[$sig->pos] = '-';
	    } else {
		$l =$m0[$sig->pos] = '0';
	    }
	    $sig->{"LOAD$l"}++;
	} else { croak("? Output $ji not mapped in minterm $m."); }        
    }

    if($ji + $jo != @pla_line){
	croak("? Bad minterm line: $_\n");
    }

    # need to detect undefined !!
    draw_minterm_row($m,@m0);


    if(eof && ($m+1) < $nc){
	$m++;
	last;
    }

    $m++;
}

#-------------------- Verilog -------------------#
# Part 3/3                                       #
#------------------------------------------------#

print VER "\n", '        $async$or$array(ors, {';
# print b's
foreach $i (1..$nc) {
    print VER "b[$i]";
        if ($i<$nc) {
		print VER ",";
    }
}
print VER "},{";
# print outputs
foreach $sig (@osigs) {
    if ($sig->buf eq 'CLOCK') {
	print VER 't'
    }
    printf VER "%s", $sig->name;
    if ($sig ne $osigs[$#osigs]) {
    	print VER ", "
    }
}
print VER "});\n";

# print ors[]=...
for ($offset = 0; $offset < $no; $offset++ ) {
  printf VER "        ors[%s] = %s\'b", $offset + 1, $nc;
  for ($k = 0; $k < $nc; $k++ ) {
    print VER "$ors[$k * $no + $offset]";
  }
  print VER ";\n";
}
print VER "\n    end\n\n";

# print input latches for clocked inputs
print VER "\n\n// Transparent latches for clocked inputs\n";
foreach $sig (@isigs) {
    if ($sig->buf eq 'CLOCK') {
        printf VER "\nalways \@(%s or %s)\n", $in_clock, $sig->name;
        printf VER "    if (%s==1)\n    begin\n", $in_clock;
        printf VER "        t%s = %s;\n    end\n", $sig->name, $sig->name;
    }
}
# print output latches for clocked outputs
print VER "\n\n// Transparent latches for clocked outputs\n";
foreach $sig (@osigs) {
    if ($sig->buf eq 'CLOCK') {
        printf VER "\nalways \@(%s or t%s)\n", $out_clock, $sig->name;
        printf VER "    if (%s==1)\n    begin\n", $out_clock;
        printf VER "       %s = t%s;\n", $sig->name, $sig->name;
        printf VER "       n%s = ~t%s;\n    end\n", $sig->name, $sig->name;
    }
	else {
        printf VER "\nalways \@(%s)\n", $sig->name;
        printf VER "       n%s = ~%s;\n", $sig->name, $sig->name;

	}
}
print VER "\nendmodule\n";
#---------------------------------------#
# End Verilog Code                      #
#---------------------------------------#


#END of MAIN PROCEDURE


sub draw_minterm_row( $@ ){
    my($m) = shift;
    my(@m0) = @_;
    my($ji,$jo,$b1);
    restore_position("minterm");
    
    #print "$m: ",@m0,"\n";

    for($ji=0;$ji<$num_inputs;$ji++){

	$b0 = $m0[$ji];

	if($b0 eq '-'){
	    get_cell("andx");
	} elsif($b0 eq '0'){
	    get_cell("and0");

	    #----------------- schematic -------------------
	    # program AND plane 0                           
	    #-----------------------------------------------
	       printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "and0" "symbol" "I%d" list(%d.5 %d) "R0")',
	   ++$instance_num, 2*$ji, $m+1);	
	   print SCH "\n";
	   	   
	} elsif($b0 eq '1'){
	    get_cell("and1");
	    #----------------- schematic -------------------
	    # program AND plane 1                           
	    #-----------------------------------------------
	        printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "and0" "symbol" "I%d" list(%d.5 %d) "MY")',
	   ++$instance_num, 2*$ji, $m+1);	
	  print SCH "\n";
	   		} else {
	    croak("? bad character in pla file '$b0'\n");
	}

	$b1 = $m&1 ? ROT0 : MIRY;
	if(!$ji){
	    draw_cell(ABOVE, $b1, 0);
	    save_position("minterm");
	} else {
	    draw_cell(TO_RIGHT, $b1, 0);
	}
    }
    for ($ji=$num_inputs; $ji<$pla_inputs; $ji++){
	get_cell("andx");
	draw_cell(TO_RIGHT, $m&1 ? ROT0 : MIRY, 0);
    }

    for ($jo=0; $jo<$num_outputs; $jo++){
	$b0 = $m0[$jo + $pla_inputs];
	if ($b0 eq '0'){   
	    if($jo == 0 && $dynamic){
		get_cell("or0sep");
	    } else {
		get_cell("or0");
	    }
	} elsif ($b0 eq '1'){	
	    if($jo == 0 && $dynamic){
		get_cell("or1sep");
	    } else {
		get_cell("or1");
	    }
	    #----------------- schematic -------------------
	    # program OR plane 1                            
	    #-----------------------------------------------
	    		    printf SCH ('dbCreateInstByMasterName(ID2 "PLA" "or0" "symbol" "I%d" list(%d.5 %d) "R0")',
	   ++$instance_num, 2*$pla_inputs+$jo, $m);	
	   print SCH "\n";
	   	   	    # These are the nets for the drain connections
	    # They are put here so that the connections stay put
	    	}
	draw_cell(TO_RIGHT, $m&1 ? ROT0 : MIRY, 0);
    }
    for ($jo=$num_outputs; $jo<$pla_outputs; $jo++){ # spare outputs
	get_cell("or0");
	draw_cell(TO_RIGHT, $m&1 ? ROT0 : MIRY, 0);
    }
} # draw_minterm_row

close SRC or croak ("? cannot close file : $srcpath\n");

# dummy minterm stuff 
#----------------- schematic -------------------
# hook up the clocks                            
#-----------------------------------------------
    $clock_offset = "-0.5";
    printf SCH ("schCreatePin(ID2 inID \"n%s\" \"input\" nil %s:-0.5 \"R0\")",$out_clock, $clock_offset); 
	print SCH "\n";

	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-0.5 %d.5:-0.5) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;
    printf SCH ("schCreatePin(ID2 inID \"%s\" \"input\" nil %s:-1 \"R0\")",$out_clock, $clock_offset); 
	print SCH "\n";
	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-1 %d.5:-1) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;
    printf SCH ("schCreatePin(ID2 inID \"n%s\" \"input\" nil %s:-1.5 \"R0\")",$in_clock, $clock_offset); 
	print SCH "\n";

	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-1.5 %d.5:-1.5) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;
    printf SCH ("schCreatePin(ID2 inID \"%s\" \"input\" nil %s:-2 \"R0\")",$in_clock, $clock_offset); 
	print SCH "\n";

	printf SCH "schCreateWire(ID2 \"route\" \"direct\" list(-0.5:-2 %d.5:-2) 0.0625
	0.0625 0.0)\n",2*$pla_inputs+$pla_outputs-2;
print SCH "dbSave(ID2)\n";
print SCH "dbClose(ID2)";
end_cell();
end_design();

close SCH or croak ("? cannot close file : $schpath\n");

close DST or croak ("? cannot close file : $dstpath\n");

# the end
