Generate symbols.
diff --git a/scripts/python-skywater-pdk/generate_metadata_json.py b/scripts/python-skywater-pdk/generate_metadata_json.py
index e02bfa1..5154e04 100755
--- a/scripts/python-skywater-pdk/generate_metadata_json.py
+++ b/scripts/python-skywater-pdk/generate_metadata_json.py
@@ -76,6 +76,10 @@
     re.compile('macro_sync'),
     re.compile('lsbuflv2hv_simple'),
     re.compile('_lp'),
+    re.compile('icecap'),
+    re.compile('inv_p'),
+    re.compile('nor_p'),
+    re.compile('nor2_p'),
 ]
 
 def should_ignore(f, x=IGNORE):
diff --git a/scripts/python-skywater-pdk/generate_symbols.sh b/scripts/python-skywater-pdk/generate_symbols.sh
new file mode 100755
index 0000000..9b45617
--- /dev/null
+++ b/scripts/python-skywater-pdk/generate_symbols.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+if [ ! -d $1/skywater-pdk/ ]; then
+	echo "Need a directory to the skywater-pdk"
+	exit 1
+fi
+
+find $1/skywater-pdk/ -name *.blackbox.v -delete
+find $1/skywater-pdk/ -name *.symbol.v -delete
+
+./generate_verilog_blackbox.py $1/skywater-pdk/libraries/*/*/cells/*
+
+exit 0
+
+for LIB in $1/skywater-pdk/libraries/*; do
+	LIBNAME=$(basename $LIB)
+
+	find $LIB -name \*.symbol.svg -delete
+	find $LIB -name \*.symbol.v -print \
+		| parallel -v symbolator --libname $LIBNAME --title -t --input \{\} \
+		; RETCODE=$?
+	if [ $RETCODE -ne 0 ]; then
+		echo "FAILURE on $LIB"
+		exit
+	fi
+done
diff --git a/scripts/python-skywater-pdk/generate_verilog_blackbox.py b/scripts/python-skywater-pdk/generate_verilog_blackbox.py
index 82d7257..05c7882 100755
--- a/scripts/python-skywater-pdk/generate_verilog_blackbox.py
+++ b/scripts/python-skywater-pdk/generate_verilog_blackbox.py
@@ -17,23 +17,27 @@
 import sys
 import textwrap
 
+from collections import defaultdict
+
 
 copyright_header = """\
-// Copyright 2020 The SkyWater PDK Authors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-//
-// SPDX-License-Identifier: Apache-2.0
+/**
+ * Copyright 2020 The SkyWater PDK Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
 """
 
 from skywater_pdk.utils import OrderedEnum
@@ -70,7 +74,7 @@
         if pg in (self.DATA_CONTROL,):
             return 'control'
         if pg in (self.CLOCK, self.CLOCK_CONTROL,):
-            return 'clocking'
+            return 'clocks'
         if pg in (self.SCAN_CHAIN,):
             return 'scanchain'
         if pg in (self.POWER_CONTROL,  self.POWER_OTHER,
@@ -122,6 +126,15 @@
         >>> PortGroup.classify('B_N')
         <PortGroup.DATA_IN_CHAR: 10>
 
+        >>> PortGroup.classify('C')
+        <PortGroup.DATA_IN_CHAR: 10>
+        >>> PortGroup.classify('C1')
+        <PortGroup.DATA_IN_CHAR: 10>
+        >>> PortGroup.classify('C1_N')
+        <PortGroup.DATA_IN_CHAR: 10>
+        >>> PortGroup.classify('C_N')
+        <PortGroup.DATA_IN_CHAR: 10>
+
         >>> PortGroup.classify('J')
         <PortGroup.DATA_IN_CHAR: 10>
         >>> PortGroup.classify('K')
@@ -245,14 +258,14 @@
         Clock Signals
         +++++++++++++
 
-        >>> PortGroup.classify('C')
-        <PortGroup.CLOCK: 50>
+        #>>> PortGroup.classify('C')
+        #<PortGroup.CLOCK: 50>
 
-        >>> PortGroup.classify('CN')
-        <PortGroup.CLOCK: 50>
+        #>>> PortGroup.classify('CN')
+        #<PortGroup.CLOCK: 50>
 
-        >>> PortGroup.classify('C_N')
-        <PortGroup.CLOCK: 50>
+        #>>> PortGroup.classify('C_N')
+        #<PortGroup.CLOCK: 50>
 
         >>> PortGroup.classify('CLK')
         <PortGroup.CLOCK: 50>
@@ -360,8 +373,7 @@
 
         name = name.upper()
         if re.search('^[A-FJKPN][0-9]?(_[NB])?$', name):
-            if name not in ('C', 'C_N'):
-                return cls.DATA_IN_CHAR
+            return cls.DATA_IN_CHAR
 
         if re.search('^[XYZQ][0-9]?(_[NB])?$', name):
             return cls.DATA_OUT_CHAR
@@ -403,7 +415,7 @@
             return cls.CLOCK
         if re.search('^((CLK)|(GCLK)|(GATE))_N$', name):
             return cls.CLOCK
-        if re.search('^[CG]_?N?$', name):
+        if re.search('^[G]_?N?$', name):
             return cls.CLOCK
 
         if re.search('^SLEEP(_[NB])?$', name):
@@ -527,16 +539,97 @@
     return "\n".join(textwrap.wrap(s, initial_indent=' * ', subsequent_indent=' * '))
 
 
+warning = """\
+WARNING: This file is autogenerated, do not modify directly!
+"""
 
-def write_verilog_header(f, define_data):
+
+def write_verilog_header(desc, f, define_data):
     f.write(copyright_header)
     f.write('\n')
     f.write(f"/**\n")
     f.write(wrap(f"{define_data['name']}: {define_data['description']}"))
     f.write('\n')
+    f.write(f" *\n")
+    f.write(wrap(desc))
+    f.write('\n')
+    f.write(f" *\n")
+    f.write(wrap(warning))
+    f.write('\n')
     f.write(f" */\n")
+    f.write('\n')
     f.write('(* blackbox *)\n')
-    f.write(f"module {define_data['library']}__{define_data['name']} (\n")
+    f.write(f"module {define_data['library']}__{define_data['name']} (")
+
+
+def write_verilog_parameters(f, define_data):
+    maxlen = {}
+    maxlen['pname'] = max([0]+[len(p[0]) for p in define_data['parameters']])
+    maxlen['ptype'] = max([0]+[len(p[1]) for p in define_data['parameters']])
+    if maxlen['pname']:
+        f.write('\n    // Parameters\n')
+    for pname, ptype in define_data['parameters']:
+        pname = pname.ljust(maxlen['pname'])
+        if maxlen['ptype']:
+            ptype = ptype.ljust(maxlen['ptype'])+ ' '
+        else:
+            ptype = ''
+        f.write(f'    parameter {ptype}{pname};\n')
+
+
+def write_verilog_supplies(f, define_data):
+    maxlen = {}
+    maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports'] if p[0] == 'power'])
+    maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'power'])
+    if maxlen['pname']:
+        f.write('\n    // Voltage supply signals\n')
+    for pclass, pname, pdir, ptype in define_data['ports']:
+        if pclass != 'power':
+            continue
+        assert ptype, (pclass, pname, pdir, ptype)
+
+        pname = pname.ljust(maxlen['pname'])
+        ptype = ptype.ljust(maxlen['ptype'])
+
+        f.write(f'    {ptype} {pname};\n')
+    f.write('\n')
+
+
+# def maxlen(ports, groups, filter=None):
+#     if filter is None:
+#         filter = {}
+#     maxl = {}
+#     for g in groups:
+#         maxl[g] = 0
+#     for p in ports:
+#         if not p:
+#             continue
+#         for i, g in enumerate(groups):
+#             if g is None:
+#                 continue
+#             if g in filter:
+#                 if p[0] not in filter[g]:
+#                     continue
+#             if not p[i]:
+#                 continue
+#             maxl[g] = max(maxl[g], len(p[i]))
+#     return maxl
+
+
+def ports_by_class(define_data):
+    ports = {
+        'power': [],
+        'signal': [],
+    }
+    maxlen = {}
+    maxlen['power']  = defaultdict(lambda: 0)
+    maxlen['signal'] = defaultdict(lambda: 0)
+    for pclass, pname, pdir, ptype in define_data['ports']:
+        ports[pclass].append((pclass, pname, pdir, ptype))
+        maxlen[pclass]['pname'] = max(maxlen[pclass]['pname'], len(pname))
+        maxlen[pclass]['pdir']  = max(maxlen[pclass]['pdir'],  len(pdir))
+        maxlen[pclass]['ptype'] = max(maxlen[pclass]['ptype'], len(ptype))
+    return ports, maxlen
 
 
 def write_blackbox(cellpath, define_data):
@@ -544,14 +637,123 @@
     assert not os.path.exists(outpath), outpath
     print("Creating", outpath)
     with open(outpath, "w+") as f:
-        write_verilog_header(f, define_data)
+        write_verilog_header(
+            "Verilog stub definition (black box without power pins).",
+            f, define_data)
+
+        ports, maxlen = ports_by_class(define_data)
+        maxlen = maxlen['signal']
+        for pclass, pname, pdir, ptype in ports['signal']:
+            pname = pname.ljust(maxlen['pname'])
+
+            if maxlen['pdir']:
+                pdir = pdir.ljust(maxlen['pdir'])+' '
+            else:
+                pdir = ''
+
+            if maxlen['ptype']:
+                ptype = ptype.ljust(maxlen['ptype'])+ ' '
+            else:
+                ptype = ''
+
+            f.write(f"\n    {pdir}{ptype}{pname},")
+        if ports['signal']:
+            f.seek(f.tell()-1)
+            f.write("\n")
+        f.write(");\n")
+
+        write_verilog_parameters(f, define_data)
+        write_verilog_supplies(f, define_data)
+        f.write('endmodule\n')
+
+
+def write_blackbox_pp(cellpath, define_data):
+    outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.pp.blackbox.v")
+    assert not os.path.exists(outpath), outpath
+    print("Creating", outpath)
+    with open(outpath, "w+") as f:
+        write_verilog_header(
+            "Verilog stub definition (black box with power pins).",
+            f, define_data)
+
+        maxlen = {}
+        maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports']])
+        maxlen['pdir']  = max([0]+[len(p[2]) for p in define_data['ports']])
+        maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'signal'])
+
+        for pclass, pname, pdir, ptype in define_data['ports']:
+            if pclass == 'power':
+                ptype = ''
+
+            pname = pname.ljust(maxlen['pname'])
+
+            if maxlen['pdir']:
+                pdir = pdir.ljust(maxlen['pdir'])+' '
+            else:
+                pdir = ''
+
+            if maxlen['ptype']:
+                ptype = ptype.ljust(maxlen['ptype'])+ ' '
+            else:
+                ptype = ''
+
+            f.write(f"\n    {pdir}{ptype}{pname},")
+        if define_data['ports']:
+            f.seek(f.tell()-1)
+            f.write("\n")
+        f.write(");\n")
+        write_verilog_parameters(f, define_data)
+        f.write('endmodule\n')
+
+
+def group_ports_for_symbol(define_data, only):
+    ports = []
+    for pclass, pname, pdir, ptype in define_data['ports']:
+        if pclass not in only:
+            continue
+        pg = PortGroup.classify(pname, define_data['name'])
+        ports.append((pg, pclass, pname, pdir, ptype))
+
+    ports.sort()
+
+    pports = [None,]
+    while len(ports) > 0:
+        if pports[-1] and pports[-1][0].type != ports[0][0].type:
+            pports.append(None)
+        pports.append(ports.pop(0))
+
+    if len(pports) == 1:
+        return []
+
+    return pports
+
+
+def write_symbol(cellpath, define_data):
+    outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.symbol.v")
+    assert not os.path.exists(outpath), outpath
+    print("Creating", outpath)
+
+    # Group the ports to make a nice symbol
+    pports = group_ports_for_symbol(define_data, ['signal'])
+
+    with open(outpath, "w+") as f:
+        write_verilog_header(
+            "Verilog stub (without power pins) for graphical symbol definition generation.",
+            f, define_data)
 
         maxlen = {}
         maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports'] if p[0] == 'signal'])
         maxlen['pdir']  = max([0]+[len(p[2]) for p in define_data['ports'] if p[0] == 'signal'])
         maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'signal'])
 
-        for pclass, pname, pdir, ptype in define_data['ports']:
+        for i, p in enumerate(pports):
+            if not p:
+                np = pports[i+1]
+                if i > 0:
+                    f.write('\n')
+                f.write('\n    //# {{'+np[0].type+'|'+np[0].desc+'}}')
+                continue
+            pg, pclass, pname, pdir, ptype = p
             if pclass != 'signal':
                 continue
 
@@ -567,122 +769,42 @@
             else:
                 ptype = ''
 
-            f.write(f"    {pdir}{ptype}{pname},\n")
-        f.seek(f.tell()-2)
-        f.write("\n);\n")
-
-        maxlen['pname'] = max([0]+[len(p[0]) for p in define_data['parameters']])
-        maxlen['ptype'] = max([0]+[len(p[1]) for p in define_data['parameters']])
-        if maxlen['pname']:
-            f.write('\n    // Parameters\n')
-        for pname, ptype in define_data['parameters']:
-            pname = pname.ljust(maxlen['pname'])
-            if maxlen['ptype']:
-                ptype = ptype.ljust(maxlen['ptype'])+ ' '
-            else:
-                ptype = ''
-            f.write(f'    parameter {ptype}{pname};\n')
-
-        maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports'] if p[0] == 'power'])
-        maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'power'])
-        if maxlen['pname']:
-            f.write('\n    // Voltage supply signals\n')
-        for pclass, pname, pdir, ptype in define_data['ports']:
-            if pclass != 'power':
-                continue
-            assert ptype, (pclass, pname, pdir, ptype)
-
-            pname = pname.ljust(maxlen['pname'])
-            ptype = ptype.ljust(maxlen['ptype'])
-
-            f.write(f'    {ptype} {pname};\n')
-
-        f.write('endmodule\n')
-
-
-def write_blackbox_pp(cellpath, define_data):
-    outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.pp.blackbox.v")
-    assert not os.path.exists(outpath), outpath
-    print("Creating", outpath)
-    with open(outpath, "w+") as f:
-        write_verilog_header(f, define_data)
-
-        maxlen = {}
-        maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports']])
-        maxlen['pdir']  = max([0]+[len(p[2]) for p in define_data['ports']])
-        maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'signal'])
-
-        for pclass, pname, pdir, ptype in define_data['ports']:
-            if pclass == 'power':
-                ptype = ''
-
-            pname = pname.ljust(maxlen['pname'])
-
-            if maxlen['pdir']:
-                pdir = pdir.ljust(maxlen['pdir'])+' '
-            else:
-                pdir = ''
-
-            if maxlen['ptype']:
-                ptype = ptype.ljust(maxlen['ptype'])+ ' '
-            else:
-                ptype = ''
-
-            f.write(f"    {pdir}{ptype}{pname},\n")
-        f.seek(f.tell()-2)
-        f.write("\n);\n")
-
-        maxlen['pname'] = max([0]+[len(p[0]) for p in define_data['parameters']])
-        maxlen['ptype'] = max([0]+[len(p[1]) for p in define_data['parameters']])
-        if maxlen['pname']:
-            f.write('\n    // Parameters\n')
-        for pname, ptype in define_data['parameters']:
-            pname = pname.ljust(maxlen['pname'])
-            if maxlen['ptype']:
-                ptype = ptype.ljust(maxlen['ptype'])+ ' '
-            else:
-                ptype = ''
-            f.write(f'    parameter {ptype}{pname};\n')
+            f.write(f"\n    {pdir}{ptype}{pname},")
+        if pports:
+            f.seek(f.tell()-1)
+            f.write("\n")
+        f.write(");\n")
+        write_verilog_parameters(f, define_data)
+        write_verilog_supplies(f, define_data)
         f.write('endmodule\n')
 
 
 def write_symbol_pp(cellpath, define_data):
     outpath = os.path.join(cellpath, f"{define_data['library']}__{define_data['name']}.pp.symbol.v")
     assert not os.path.exists(outpath), outpath
-
-    ports = []
-    for pclass, pname, pdir, ptype in define_data['ports']:
-        pg = PortGroup.classify(pname, define_data['name'])
-        ports.append((pg, pclass, pname, pdir, ptype))
-
-    ports.sort()
-
-    pports = [None,]
-    while len(ports) > 0:
-        if pports[-1] and pports[-1][0].type != ports[0][0].type:
-            pports.append(None)
-        pports.append(ports.pop(0))
-
-    for i, p in enumerate(pports):
-        if not p:
-            np = pports[i+1]
-            print('//# {{'+np[0].type+'|'+np[0].desc+'}}')
-            continue
-        print(p)
-
-    return
-
-
     print("Creating", outpath)
+
+    # Group the ports to make a nice symbol
+    pports = group_ports_for_symbol(define_data, ['signal', 'power'])
+
     with open(outpath, "w+") as f:
-        write_verilog_header(f, define_data)
+        write_verilog_header(
+            "Verilog stub (with power pins) for graphical symbol definition generation.",
+            f, define_data)
 
         maxlen = {}
         maxlen['pname'] = max([0]+[len(p[1]) for p in define_data['ports']])
         maxlen['pdir']  = max([0]+[len(p[2]) for p in define_data['ports']])
         maxlen['ptype'] = max([0]+[len(p[3]) for p in define_data['ports'] if p[0] == 'signal'])
 
-        for pclass, pname, pdir, ptype in define_data['ports']:
+        for i, p in enumerate(pports):
+            if not p:
+                np = pports[i+1]
+                if i > 0:
+                    f.write('\n')
+                f.write('\n    //# {{'+np[0].type+'|'+np[0].desc+'}}')
+                continue
+            pg, pclass, pname, pdir, ptype = p
             if pclass == 'power':
                 ptype = ''
 
@@ -698,25 +820,17 @@
             else:
                 ptype = ''
 
-            f.write(f"    {pdir}{ptype}{pname},\n")
-        f.seek(f.tell()-2)
-        f.write("\n);\n")
-
-        maxlen['pname'] = max([0]+[len(p[0]) for p in define_data['parameters']])
-        maxlen['ptype'] = max([0]+[len(p[1]) for p in define_data['parameters']])
-        if maxlen['pname']:
-            f.write('\n    // Parameters\n')
-        for pname, ptype in define_data['parameters']:
-            pname = pname.ljust(maxlen['pname'])
-            if maxlen['ptype']:
-                ptype = ptype.ljust(maxlen['ptype'])+ ' '
-            else:
-                ptype = ''
-            f.write(f'    parameter {ptype}{pname};\n')
+            f.write(f"\n    {pdir}{ptype}{pname},")
+        if pports:
+            f.seek(f.tell()-1)
+            f.write("\n")
+        f.write(");\n")
+        write_verilog_parameters(f, define_data)
         f.write('endmodule\n')
 
 
 
+
 def process(cellpath):
     print()
     print(cellpath)
@@ -726,17 +840,14 @@
         return
     assert os.path.exists(define_json), define_json
     define_data = json.load(open(define_json))
-    pprint.pprint(define_data)
 
     write_blackbox(cellpath, define_data)
     write_blackbox_pp(cellpath, define_data)
 
+    write_symbol(cellpath, define_data)
     write_symbol_pp(cellpath, define_data)
     pprint.pprint(drive_strengths(define_data['name'], cellpath))
 
-    for pclass, pname, pdir, ptype in define_data['ports']:
-        print(pname, PortGroup.classify(pname, define_data['name']))
-
     return
     drives = define_data.get('drives', [])
     for d in drives: