Added a DRC expander
diff --git a/scripts/drcexpander.pl b/scripts/drcexpander.pl
new file mode 100644
index 0000000..8a70d45
--- /dev/null
+++ b/scripts/drcexpander.pl
@@ -0,0 +1,324 @@
+print STDERR "DRC Expander - expands the DRC rules in MAGIC tech files and annotates the layers that might cause the DRC issues\n";
+
+# Original tech file:
+my $tech=$ARGV[0] || "/usr/local/lib/magic/sys/sky130A.tech";
+my $expand=0;
+my $debug=0;
+my $createrules=0; # Create code for rules instead of expanding the tech file
+
+print STDERR "Processing the original tech file: $tech\n";
+
+#our %contacts=();
+our %alias=();
+
+# Required DRC rules for SKY130: area,cifmaxwidth,edge4way,exact_overlap,extend,overhang,rect_only,spacing,surround,width
+my $rules=<<EOF
+surround types1 types2 distance presence error
+widespacing types1 wwidth types2 distance flavor error
+spacing types1 types2 distance adjacency error
+width type-list width error
+overhang types1 types2 distance error
+extend types1 types2 distance error
+rect_only types error
+angles types allowed why
+edge types1 types2 d OKTypes cornerTypes cornerDist error [plane]
+edge4way types1 types2 d OKTypes cornerTypes cornerDist error [plane]
+exact_overlap type-list
+no_overlap type-list1 type-list2
+off_grid types pitch why
+area types minarea minedge why
+maxwidth layers mwidth [bends] why
+cifwidth layer width why
+cifspacing layer1 layer2 separation adjacency why
+cifarea layer minarea minedge why
+cifmaxwidth layer mwidth [bends] why
+EOF
+;
+
+print STDERR "Extracting optional arguments:\n";
+my @erules=();
+foreach my $line(split "\n",$rules)
+{
+ if($line=~m/\[/)
+ {
+ my $a=$line; $a=~s/\[\w+\]//;
+ my $b=$line; $b=~s/\[//; $b=~s/\]//;
+ #print "A: $a\nB: $b\n";
+ push @erules,$a;
+ push @erules,$b;
+ }
+ else
+ {
+ #print "R: $line\n";
+ push @erules,$line;
+ }
+}
+
+print STDERR "Handling rules:\n";
+foreach my $rule (@erules)
+{
+ print STDERR " # Rule: $rule\n";
+ my @a=split " ",$rule;
+ my $kind=shift @a;
+ #print "Kind:$kind\n";
+ my $regexp="^\\s*($kind)";
+ my @types=("zero","other");
+ my @typelists=();
+ foreach(@a)
+ {
+ $regexp.="\\s+";
+ if($_=~m/^(types|type-list|layer|OKTypes)/)
+ {
+ $regexp.="(\\S+)";
+ push @types,"type-list";
+ }
+ elsif($_=~m/^(why|error)/)
+ {
+ $regexp.="\"([^\"]*)\"";
+ push @types,"why";
+ }
+ else
+ {
+ $regexp.="(\\S+)";
+ push @types,"other";
+ }
+ }
+
+ next unless($createrules);
+
+ $regexp.="\\s*\$";
+ print " if(m/$regexp/)\n {\n";
+ print ' print " # ORIGINAL RULE:$oneline\n";'."\n";
+ foreach(2 .. scalar(@types)-1)
+ {
+ print " my \$vl$_=\$$_;\n" if($types[$_] eq "type-list");
+ print " my \$l$_=join \" \",allLayers(\$vl$_);\n" if($types[$_] eq "type-list");
+ # print " print STDERR \"L$_: \$vl$_ -> \$l$_\n\" if(\$debug);\n" if($types[$_] eq "type-list");
+ print " my \$text=\$$_;\n" if($types[$_] eq "why");
+ push @typelists,$_ if($types[$_] eq "type-list");
+ }
+ print " print \" ";
+ foreach(1 .. scalar(@types)-1)
+ {
+ print "\$$_ " if($types[$_] eq "other");
+ print "\$vl$_ " if($types[$_] eq "type-list");
+ if($types[$_] eq "why")
+ {
+ print "\\\"\$$_ ";
+ #print "[erase ".join(",",@typelists)."]";
+ foreach my $tl (@typelists)
+ {
+ print "[erase \$l$tl]";
+ }
+ print "\\\"";
+ }
+ }
+ print "\\n\";\n";
+ print " }\n";
+ print "\n";
+ # $1 $vl1 $vl2 $4 $5 \"$6 [erase $l1]".(($vl1 eq $vl2)?"":"[erase $l2]")."\"\n";
+
+
+}
+
+exit if($createrules);
+
+#magic: tech layers allli
+# At first we are reading through the original tech file to search for all the virtual layers that need to be expanded
+open IN,"<$tech";
+while(<IN>)
+{
+ s/\\//g;
+ if(m/^\s*(spacing|surround)\s+(\S+)\s+(\S+)\s+/)
+ {
+ $alias{$2}=1;
+ $alias{$3}=1;
+ foreach(split(",",$2))
+ {
+ $alias{$_}=1;
+ }
+ foreach(split(",",$3))
+ {
+ $alias{$_}=1;
+ }
+ }
+}
+close IN;
+
+
+if($debug)
+{
+ print STDERR "BEFORE:\n";
+ foreach(sort keys %alias)
+ {
+ print STDERR "$_ -> $alias{$_}\n";
+ }
+}
+
+# No we are asking magic what those virtual layers actually mean
+ if(open OUT,"|magic -dnull -noconsole -T $tech >magic.layers.out")
+ {
+ print OUT "puts \"CUT HERE -------- CUT HERE\"\n";
+ foreach(sort keys %alias)
+ {
+ print OUT "puts \"EXPANDING: $_\"\n";
+ print OUT "puts [tech layers $_]\n";
+ }
+ print OUT "puts \"CUT HERE -------- CUT HERE\"\n";
+ print OUT "quit -noprompt\n";
+ close OUT;
+
+ # Now we are retrieving the answer from Magic:
+ if(open IN,"<magic.layers.out")
+ {
+ while(<IN>)
+ {
+ if(m/^EXPANDING: (\S+)/)
+ {
+ my $next=<IN>;
+ chomp $next;
+ $alias{$1}=$next;
+ }
+ }
+ close IN;
+ }
+ else
+ {
+ die "Could not get the results from magic!\n";
+ }
+ }
+ else
+ {
+ die "Could not run magic!\n";
+ }
+
+
+if($debug)
+{
+ print STDERR "\n\nAFTER:\n";
+ foreach(sort keys %alias)
+ {
+ print STDERR "$_ -> $alias{$_}\n";
+ }
+ exit;
+}
+
+open IN,"<$tech";
+
+# This function expands a single layer-list into all the physical layers it means
+sub allLayers($)
+{
+ #if($_[0] !~ m/\//) # If we do not look for "images" (which are signalled by / in the typelist and are currently not supported by magic), then we can just use the whole typelist which is more efficient.
+ #{
+ #return split " ",$alias{$_[0]} ;
+ #}
+
+ my @a=split ",",$_[0];
+ my %b=();
+ foreach(@a)
+ {
+ #print STDERR "piece: $_\n";
+ if(m/(\/\S+)/ && defined($alias{$_}))
+ {
+ my $image=$1;
+ foreach my $part(split " ",$alias{$_})
+ {
+ $b{$part.(($part =~ m/\//)?"":$image)}=1;
+ }
+ }
+ else
+ {
+ foreach my $part(split " ",$alias{$_})
+ {
+ $b{$part}=1;
+ }
+ }
+ }
+ return sort keys %b;
+}
+
+my $section="";
+our %drckind=();
+# Finally we are processing the whole tech file and annotating the rules:
+while(<IN>)
+{
+ while($_=~m/\\$/)
+ {
+ $_.=<IN>;
+ }
+ if(m/^(\w+)/)
+ {
+ $section=$1;
+ }
+ # if($section eq "contact" && m/^\s+(\w+)/)
+ #{
+ # my $a=$_;
+ # $a=~s/^\s+//; $a=~s/\s+$//;
+ # my @a=split(/\s+/,$a);
+ # my $cont=shift(@a);
+ # foreach my $cnt(@a)
+ # {
+ # print STDERR "CONTACT: $cont -> $cnt\n";
+ # $contacts{$cont}{$cnt}=1;
+ # $contacts{$cnt}{$cont}=1;
+ # }
+ #}
+ #if($section eq "aliases" && m/^\s+(\w+)/)
+ #{
+ # my $a=$_;
+ # $a=~s/^\s+//; $a=~s/\s+$//;
+ # my @a=split(/\s+/,$a);
+ # my $cont=shift(@a);
+ # foreach my $cnt(@a)
+ # {
+ # print STDERR "ALIAS: $cont -> $cnt\n";
+ # $alias{$cont}=$cnt;
+ # }
+ #}
+ my $oneline=$_;
+ $oneline=~s/\n//sg;
+ tr/\\//;
+ if($section eq "drc")
+ {
+ if(m/^\s*(\w+)/)
+ {
+ $drckind{$1}=1;
+ }
+ }
+
+
+ if(m/^\s*(spacing|surround)\s+(\S+)\s+(\S+)\s+(\d+)\s+\\?\s*(\w+)\s+\\?\s*"([^"]+)"/)
+ {
+ #print " # ORIGINAL RULE:$oneline\n";
+ my $vl1=$2;
+ my $vl2=$3;
+ my $text=$6;
+ my $l1=join " ",allLayers($vl1);
+ print STDERR "L1: $vl1 -> $l1\n" if($debug);
+ my $l2=join " ",allLayers($vl2);
+ print STDERR "L2: $vl2 -> $l2\n" if($debug);
+
+ if($expand)
+ {
+ foreach my $layer1(allLayers($vl1))
+ {
+ foreach my $layer2(allLayers($vl2))
+ {
+ #print " $1 $layer1 $layer2 $4 $5 \"$6 [erase $layer1]".(($layer1 eq $layer2)?"":"[erase $layer2]")."\"\n";
+ }
+ }
+ }
+ else
+ {
+ print " $1 $vl1 $vl2 $4 $5 \"$6 [erase $l1".(($vl1 eq $vl2)?"":",$l2")."]\"\n";
+ }
+ #print "\n";
+ }
+ else
+ {
+ print $_;
+ }
+}
+close IN;
+
+print STDERR "".join(",", sort keys %drckind)."\n";