Corrected one module in sky130_ef_io.v where three lines got
inadvertently repeated, causing an error with port assignment and
when running iverilog.
diff --git a/VERSION b/VERSION
index de5c9be..65dac43 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.428
+1.0.429
diff --git a/common/create_spice_library.py b/common/create_spice_library.py
index 2f87a52..b2ed037 100755
--- a/common/create_spice_library.py
+++ b/common/create_spice_library.py
@@ -44,7 +44,7 @@
 
     fformat = 'CDL' if spiext == '.cdl' else 'SPICE'
 
-    allstubname = destlibdir + '/stub' + spiext
+    allstubname = destlibdir + '/' + destlibroot + '__blackbox' + spiext
     alllibname = destlibdir + '/' + destlibroot + spiext
     if do_stub:
         outputname = allstubname
@@ -107,6 +107,8 @@
                     sseen = list(item for item in subckts if item in allsubckts)
                     allsubckts.extend(list(item for item in subckts if item not in allsubckts))
                     sfilter = remove_redundant_subckts(stext, subckts, sseen)
+                    if do_stub:
+                        sfilter = create_blackboxes(sfilter)
                     print(sfilter, file=ofile)
                 print('\n******* EOF\n', file=ofile)
 
@@ -121,6 +123,16 @@
         print('Only one file (' + str(slist) + ');  ignoring "compile" option.')
 
 #----------------------------------------------------------------------------
+# Reduce a SPICE or CDL netlist file to blackbox entries
+#----------------------------------------------------------------------------
+
+def create_blackboxes(ntext):
+    # This remains to be done;  something a bit more rigorous than
+    # runtime/makestub.py
+    updated = ntext
+    return updated
+
+#----------------------------------------------------------------------------
 # Remove redundant subcircuit entries from a SPICE or CDL netlist file.  "sseen"
 # is a list of subcircuit names gleaned from all previously read files using
 # re.findall(). "slist" is a list of subcircuits including those in "ntext".
diff --git a/common/create_verilog_library.py b/common/create_verilog_library.py
index fce0c2a..3761540 100755
--- a/common/create_verilog_library.py
+++ b/common/create_verilog_library.py
@@ -30,7 +30,7 @@
     print('    <destlibdir>      is the directory containing the individual files')
     print('    <destlib>         is the root name of the library file')
     print('    -compile-only     remove the indidual files if specified')
-    print('    -stub             generate only the module headers for each cell')
+    print('    -stub             generate file of blackbox modules for each cell')
     print('    -excludelist=     is a comma-separated list of files to ignore')
     print('')
 
@@ -41,10 +41,16 @@
     # 'destlib' should not have an extension, because one will be generated.
     destlibroot = os.path.splitext(destlib)[0]
 
+    allstubname = destlibdir + '/' + destlibroot + '__blackbox.v'
     alllibname = destlibdir + '/' + destlibroot + '.v'
+
     if os.path.isfile(alllibname):
         os.remove(alllibname)
 
+    if do_stub:
+        if os.path.isfile(allstubname):
+            os.remove(allstubname)
+
     print('Diagnostic:  Creating consolidated verilog library ' + destlibroot + '.v')
 
     # If file "filelist.txt" exists in the directory, get the list of files from it
@@ -77,6 +83,8 @@
 
     if len(vlist) > 1:
         print('New file is:  ' + alllibname)
+        if do_stub:
+            sfile = open(allstubname, 'w')
         with open(alllibname, 'w') as ofile:
             allmodules = []
             for vfile in vlist:
@@ -93,6 +101,9 @@
                     # not checking if duplicate modules might exist within separate
                     # blocks of an "ifdef".
                     # vfilter = remove_redundant_modules(vtext, modules, mseen)
+                    if do_stub:
+                        slines = create_blackboxes(vtext)
+                        print(slines, file=sfile)
                     vfilter = vtext
 
                     # NOTE:  The following workaround resolves an issue with iverilog,
@@ -102,6 +113,9 @@
                     print(vlines, file=ofile)
                 print('\n//--------EOF---------\n', file=ofile)
 
+        if do_stub:
+            sfile.close()
+
         if do_compile_only == True:
             print('Compile-only:  Removing individual verilog files')
             for vfile in vlist:
@@ -113,6 +127,73 @@
         print('Only one file (' + str(vlist) + ');  ignoring "compile" option.')
 
 #----------------------------------------------------------------------------
+# Reduce a verilog file to blackbox modules
+#----------------------------------------------------------------------------
+
+def create_blackboxes(ntext):
+    updated = []
+
+    modrex = re.compile('^[ \t]*module[ \t\n]+')
+    endrex = re.compile('^[ \t]*endmodule[ \t\n]+')
+    inrex = re.compile('^[ \t]*input[ \t\n]+')
+    outrex = re.compile('^[ \t]*output[ \t\n]+')
+    inoutrex = re.compile('^[ \t]*inout[ \t\n]+')
+    ifrex = re.compile('^[ \t]*`if')
+    elsrex = re.compile('^[ \t]*`els')
+    endifrex = re.compile('^[ \t]*`end')
+    suprex = re.compile('^[ \t]*supply')
+
+    in_mod = False
+    depth = 0
+    for line in ntext.splitlines():
+        # Strip any comments from line before checking
+        nocline = re.sub('[ \t]*//.*', '', line)
+        if not in_mod:
+            mmatch = modrex.match(nocline)
+            if mmatch:
+                in_mod = True
+                updated.append('(* blackbox *)')
+                depth = 1
+            updated.append(line)
+                
+        else:
+            # "module" appearing inside a module means that there are
+            # multiple module definitions in ifdef blocks.  Ideally
+            # the ifdef blocks should be tracked, instead of this bit
+            # of a hack.
+            mmatch = modrex.match(nocline)
+            if mmatch:
+                updated.append('(* blackbox *)')
+                depth += 1
+            ematch = endrex.match(nocline)
+            if ematch:
+                depth -= 1
+                if depth == 0:
+                    in_mod = False
+                updated.append(line)
+            else:
+                # input, output, and inout lines remain in module output
+                t1match = inrex.match(nocline)
+                t2match = outrex.match(nocline)
+                t3match = inoutrex.match(nocline)
+                tmatch = t1match or t2match or t3match
+
+                # ifdef blocks remain in module output because they may
+                # modify the set of I/Os.
+                i1match = ifrex.match(nocline)
+                i2match = elsrex.match(nocline)
+                i3match = endifrex.match(nocline)
+                imatch = i1match or i2match or i3match
+
+                # supply1/supply0 lines remain in blackbox module output
+                smatch = suprex.match(nocline)
+
+                if tmatch or imatch or smatch:
+                    updated.append(line)
+
+    return '\n'.join(updated)
+
+#----------------------------------------------------------------------------
 # Remove redundant module entries from a verilog file.  "m2list" is a list of
 # module names gleaned from all previously read files using re.findall().
 # "mlist" is a list of all module names including those in "ntext".
diff --git a/gf180mcu/Makefile.in b/gf180mcu/Makefile.in
index 865f3e5..f2f6a79 100644
--- a/gf180mcu/Makefile.in
+++ b/gf180mcu/Makefile.in
@@ -999,7 +999,7 @@
 		-lef cells/*/*.lef annotate compile-only \
 			filter=custom/scripts/fix_digital_lef.py \
 		-verilog models/*/*.v compile-only rename=primitives \
-		-verilog cells/*/*.v exclude=*.*.v,primitives.v \
+		-verilog cells/*/*.v exclude=*.*.v,primitives.v stub \
 			compile-only filter=custom/scripts/inc_verilog.py \
 		-library digital gf180mcu_fd_sc_mcu9t5v0 2>&1 | \
 		tee -a ${GF180MCU$*}_make.log
@@ -1083,7 +1083,7 @@
 		-lef cells/*/*.lef annotate compile-only \
 			filter=custom/scripts/fix_digital_lef.py \
 		-verilog models/*/*.v compile-only rename=primitives \
-		-verilog cells/*/*.v exclude=*.*.v,primitives.v \
+		-verilog cells/*/*.v exclude=*.*.v,primitives.v stub \
 			compile-only filter=custom/scripts/inc_verilog.py \
 		-library digital gf180mcu_fd_sc_mcu7t5v0 2>&1 | \
 		tee -a ${GF180MCU$*}_make.log
diff --git a/sky130/custom/sky130_fd_io/verilog/sky130_ef_io.v b/sky130/custom/sky130_fd_io/verilog/sky130_ef_io.v
index 893c550..8b7c228 100644
--- a/sky130/custom/sky130_fd_io/verilog/sky130_ef_io.v
+++ b/sky130/custom/sky130_fd_io/verilog/sky130_ef_io.v
@@ -1360,9 +1360,6 @@
 		.OGC_HVC(),
 		.DRN_HVC(DRN_HVC),
 		.SRC_BDY_HVC(SRC_BDY_HVC),
-		.OGC_HVC(),
-		.DRN_HVC(DRN_HVC),
-		.SRC_BDY_HVC(SRC_BDY_HVC),
 	`endif // USE_POWER_PINS
 	.P_PAD(P_PAD),
 	.AMUXBUS_A(AMUXBUS_A),