Added support for bipolar transistor types (using the pwell or
nwell base layer as the transistor ID type) and bipolar transistor
extraction.
diff --git a/VERSION b/VERSION
index 15245f3..e8eb7f1 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.32
+1.0.33
diff --git a/common/spectre_to_spice.py b/common/spectre_to_spice.py
index fdbfe31..6d137d5 100755
--- a/common/spectre_to_spice.py
+++ b/common/spectre_to_spice.py
@@ -104,14 +104,17 @@
                 rmatch = rtok.match(rest)
                 if rmatch:
                     expch = rmatch.group(1)[0]
-                    if (expch.isalpha() or expch == '$') and not needmore:
+                    if expch == '$':
+                        break
+                    elif expch.isalpha() and not needmore:
                         break
                     else:
                         needmore = False
                         value += rmatch.group(1)
                         rest = rmatch.group(2)
-                        expch = rmatch.group(1).strip()
-                        if expch in '+-*/(){}^~!':
+                        if any((c in '+-*/({^~!') for c in rmatch.group(1)[-1]):
+                            needmore = True
+                        if rest != '' and any((c in '+-*/(){}^~!') for c in rest[0]):
                             needmore = True
                 else:
                     break
@@ -121,7 +124,14 @@
             elif value.strip().startswith("'"):
                 fmtline.append(value)
             else:
-                fmtline.append('{' + value + '}')
+                # It is not possible to know if a spectre expression continues
+                # on another line without some kind of look-ahead, but check
+                # if the parameter ends in an operator.
+                lastc = value.strip()[-1]
+                if any((c in '*+-/,(') for c in lastc):
+                    fmtline.append('{' + value)
+                else:
+                    fmtline.append('{' + value + '}')
 
             # These parameter sub-expressions are related to monte carlo
             # simulation and are incompatible with ngspice.  So put them
@@ -141,15 +151,39 @@
             # assumes that the parameter is always passed, and therefore must
             # be part of the .subckt line.  A parameter without a value is not
             # legal SPICE, so supply a default value of 1.
+
             pmatch = parm5rex.match(rest)
             if pmatch:
-                fmtline.append(pmatch.group(1) + '=1')
-                ispassed = True
-                rest = pmatch.group(2)
+                # NOTE: Something that is not a parameters name should be
+                # extended from the previous line.  Note that this parsing
+                # is not rigorous and is possible to break. . .
+                if any((c in '+-*/(){}^~!') for c in pmatch.group(1).strip()):
+                    fmtline.append(rest)
+                    if not any((c in '*+-/,(') for c in rest.strip()[-1]):
+                        fmtline.append('}')
+                    rest = ''
+                else:
+                    fmtline.append(pmatch.group(1) + '=1')
+                    ispassed = True
+                    rest = pmatch.group(2)
             else:
                 break
 
-    return ' '.join(fmtline), ispassed
+    finalline = ' '.join(fmtline)
+
+    # ngspice does not understand round(), so replace it with the equivalent
+    # floor() expression.
+
+    finalline = re.sub('round\(', 'floor(0.5+', finalline)
+
+    # use of "no" and "yes" as parameter values is not allowed in ngspice.
+
+    finalline = re.sub('sw_et[ \t]*=[ \t]*{no}', 'sw_et=0', finalline)
+    finalline = re.sub('sw_et[ \t]*=[ \t]*{yes}', 'sw_et=1', finalline)
+    finalline = re.sub('isnoisy[ \t]*=[ \t]*{no}', 'isnoisy=0', finalline)
+    finalline = re.sub('isnoisy[ \t]*=[ \t]*{yes}', 'isnoisy=1', finalline)
+
+    return finalline, ispassed
 
 def get_param_names(line):
     # Find parameter names in a ".param" line and return a list of them.
@@ -357,7 +391,7 @@
                 # Continue to "if inmodel == 1" block below
             else:
                 fmtline, ispassed = parse_param_line(mmatch.group(3), True, False, True, ispassed)
-                if isspectre and (modtype == 'resistor'):
+                if isspectre and (modtype == 'resistor' or modtype == 'r2'):
                     modtype = 'r'
                 modellines.append('.model ' + modname + ' ' + modtype + ' ' + fmtline)
                 if fmtline != '':
@@ -533,7 +567,7 @@
 
             cmatch = cdlrex.match(line)
             if not cmatch:
-                if not isspectre:
+                if not isspectre or 'capacitor' in line or 'resistor' in line:
                     cmatch = stddevrex.match(line)
 
             if cmatch:
@@ -548,12 +582,6 @@
                 elif devmodel == 'resistor':
                     devtype = 'r'
                     devmodel = ''
-                elif devmodel == 'resbody':
-                    # This is specific to the SkyWater models;  handling it
-                    # in a generic way would be difficult, as it would be
-                    # necessary to find the model and discover that the
-                    # model is a resistor and not a subcircuit.
-                    devtype = 'r'
                 elif devtype.lower() == 'n' or devtype.lower() == 'p':
                     # May be specific to SkyWater models, or is it a spectreism?
                     # NOTE:  There is a check, below, to revisit this assignment
@@ -651,6 +679,13 @@
         # Copy line as-is
         spicelines.append(line)
 
+    # If any model lines remain at end, append them before output
+    if modellines != []:
+        for line in modellines:
+            spicelines.append(line)
+        modellines = []
+        inmodel = False
+
     # Output the result to out_file.
     with open(out_file, 'w') as ofile:
         for line in spicelines:
diff --git a/sky130/magic/sky130.tech b/sky130/magic/sky130.tech
index 193b56f..4b5ef0e 100644
--- a/sky130/magic/sky130.tech
+++ b/sky130/magic/sky130.tech
@@ -64,6 +64,9 @@
 # sky130_fd_pr__diode_pw2nd_lvt	ndiodelvt	low Vt n+ diff diode
 # sky130_fd_pr__diode_pd2nw_lvt	pdiodelvt	low Vt p+ diff diode
 # sky130_fd_pr__diode_pd2nw_hvt	pdiodehvt	high Vt p+ diff diode
+# sky130_fd_pr__npn_05v0	pbase		NPN in deep nwell
+# sky130_fd_pr__npn_11v0	mvpbase		thick oxide gated NPN
+# sky130_fd_pr__pnp_05v0	nbase		PNP
 # xcmimc1			mimcap		MiM cap 1st plate
 # xcmimc2			mimcap2		MiM cap 2nd plate
 # mrdn				rdn		n+ diff resistor
@@ -104,10 +107,6 @@
 # nhvnativeesd			ESD native nFET
 # phvesd			ESD thickox pFET
 # fnpass			flash nFET device
-# npnpar1x*			parasitic NPN
-# npn_1x1_2p0_hv		thickox gated parasitic NPN
-# pnppar			parasitic PNP
-# pnppar5x			parasitic PNP
 # xesd_ndiode_h_***		ESD n+ diode
 # xesd_pdiode_h_***		ESD p+ diode
 # reslocsub			local substrate island indicator
@@ -162,6 +161,9 @@
  -well pwell,pw
  -well rpw,rpwell
  -well obswell
+ -well pbase,npn
+ -well mvpbase,mvnpn
+ -well nbase,pnp
 
 # Transistors
   active nmos,ntransistor,nfet
@@ -373,7 +375,7 @@
 aliases
 
   allwellplane     nwell
-  allnwell	   nwell,obswell
+  allnwell	   nwell,obswell,pnp
 
   allnfets	   nfet,npass,npd,scnfet,mvnfet,mvnnfet,nfetlvt,nsonos
   allpfets	   pfet,ppu,scpfet,mvpfet,pfethvt,pfetlvt,pfetmvt
@@ -477,6 +479,10 @@
   nsc       ndiff_in_nwell metal1  contact_X'es
   psc       pdiff_in_pwell metal1  contact_X'es
 
+  pnp	    nwell ntransistor_stripes
+  npn	    pwell ptransistor_stripes
+  mvnpn	    pwell hvpdiff_mask
+
   pfetlvt   ptransistor	ptransistor_stripes implant1
   pfetmvt   ptransistor	ptransistor_stripes implant3
   pfethvt   ptransistor ptransistor_stripes implant2
@@ -656,8 +662,8 @@
 #-----------------------------------------------------
 
 connect
-  *nwell,*nsd,*mvnsd,dnwell *nwell,*nsd,*mvnsd,dnwell
-  pwell,*psd,*mvpsd  pwell,*psd,*mvpsd
+  *nwell,*nsd,*mvnsd,dnwell,pnp *nwell,*nsd,*mvnsd,dnwell,pnp
+  pwell,*psd,*mvpsd,npn  pwell,*psd,*mvpsd,npn
   *li,coreli	*li,coreli
   *m1,m1fill	*m1,m1fill
   *m2,m2fill	*m2,m2fill
@@ -724,7 +730,7 @@
 # DNWELL
 #----------------------------------------------------------------
 
- layer DNWELL	dnwell
+ layer DNWELL	dnwell,pnp
 	calma	64 18
 
  layer PWRES    rpw
@@ -879,6 +885,22 @@
 	calma 81 4
 
 #----------------------------------------------------------------
+# NPNID and PNPID apply to bipolar transistors
+#----------------------------------------------------------------
+
+ layer NPNID
+	bloat-all npn,mvnpn dnwell
+	calma	82 20
+
+ templayer pnparea pnp
+	grow 400
+
+ layer PNPID
+	bloat-all pnparea *psd
+	or pnparea
+	calma	82 44
+
+#----------------------------------------------------------------
 # RPM
 #----------------------------------------------------------------
 
@@ -1910,8 +1932,6 @@
 #endif
  ignore NPC
  ignore SEALID
- ignore NPNID
- ignore PNPID
  ignore CAPID
  ignore LDNTM
  ignore HVNTM
@@ -1919,6 +1939,13 @@
  ignore LOWTAPDENSITY
 
  layer nwell NWELL,WELLTXT,WELLPIN
+ and-not PNPID
+ labels NWELL
+ labels WELLTXT text
+ labels WELLPIN port
+
+ layer pnp NWELL,WELLTXT,WELLPIN
+ and PNPID
  labels NWELL
  labels WELLTXT text
  labels WELLPIN port
@@ -1930,6 +1957,10 @@
  layer dnwell DNWELL
  labels DNWELL
 
+ layer npn DNWELL
+ and-not NWELL
+ and NPNID
+
  layer rpw PWRES
  and DNWELL
  labels PWRES
@@ -3246,7 +3277,7 @@
  # Bipolar NPN mark
  calma NPNID 82 20
  # Bipolar PNP mark
- calma PNPID 82 20
+ calma PNPID 82 44
  # Capacitor ID
  calma CAPID 82 64
  # Core area ID mark
@@ -3357,8 +3388,6 @@
  calma FILLOBSM3  107 24
  calma FILLOBSM4  112 4
 
-end
-
 #-----------------------------------------------------------------------
 
 style  vendorimport
@@ -3377,8 +3406,6 @@
 #endif
  ignore NPC
  ignore SEALID
- ignore NPNID
- ignore PNPID
  ignore CAPID
  ignore LDNTM
  ignore HVNTM
@@ -3386,6 +3413,13 @@
  ignore LOWTAPDENSITY
 
  layer nwell NWELL,WELLTXT,WELLPIN
+ and-not PNPID
+ labels NWELL
+ labels WELLTXT port
+ labels WELLPIN port
+
+ layer pnp NWELL,WELLTXT,WELLPIN
+ and PNPID
  labels NWELL
  labels WELLTXT port
  labels WELLPIN port
@@ -3397,6 +3431,10 @@
  layer dnwell DNWELL
  labels DNWELL
 
+ layer npn DNWELL
+ and-not NWELL
+ and NPNID
+
  layer rpw PWRES
  and DNWELL
  labels PWRES
@@ -4713,7 +4751,7 @@
  # Bipolar NPN mark
  calma NPNID 82 20
  # Bipolar PNP mark
- calma PNPID 82 20
+ calma PNPID 82 44
  # Capacitor ID
  calma CAPID 82 64
  # Core area ID mark
@@ -5870,6 +5908,9 @@
  device msubcircuit sky130_fd_pr__nfet_05v0_nvt mvnnfet \
 	*mvndiff,mvndiffres *mvndiff,mvndiffres pwell,space/w error l=l w=w
 
+ device msubcircuit sky130_fd_pr__npn_05v0 npn dnwell *ndiff space/w error a1=area
+ device msubcircuit sky130_fd_pr__pnp_05v0 pnp pwell,space/w *pdiff a1=area
+
  device rsubcircuit short rmp \
 	*poly	 space/w,pwell,nwell error l=l w=w
  device rsubcircuit short rli1 \
@@ -5928,27 +5969,27 @@
 	*mvpdiff nwell   error l=l w=w
 
  device subcircuit  sky130-fd_pr__diode_pd2nw *pdiode \
-	nwell a=a p=p
+	nwell a=area
  device msubcircuit sky130_fd_pr__diode_pw2nd *ndiode \
-	pwell,space/w a=a p=p
+	pwell,space/w a=area
  device subcircuit  pdiode_h *mvpdiode \
-	nwell a=a p=p
+	nwell a=area
  device msubcircuit ndiode_h *mvndiode \
-	pwell,space/w a=a p=p
+	pwell,space/w a=area
 
  # These are parasitic devices
  device msubcircuit sky130_fd_pr__diode_pw2nd_lvt *ndiodelvt \
-	pwell,space/w a=a p=p
+	pwell,space/w a=area
  device subcircuit  sky130_fd_pr__diode_pd2nw_lvt *pdiodelvt \
-	nwell a=a p=p
+	nwell a=area
  device subcircuit  sky130_fd_pr_diode_pd2nw_hvt *pdiodehvt \
-	nwell a=a p=p
+	nwell a=area
  device msubcircuit sky130_fd_pr__diode_pw2nd_nvt *nndiode \
-	pwell,space/w a=a p=p
+	pwell,space/w a=area
 
 #ifdef MIM
- device subcircuit xcmimc1 *mimcap  m3 nwell,pwell,space/w error a=a p=p s=subs
- device subcircuit xcmimc2 *mimcap2 m4,mimcc/m4 nwell,pwell,space/w error a=a p=p s=subs
+ device subcircuit xcmimc1 *mimcap  m3 nwell,pwell,space/w error a=area s=subs
+ device subcircuit xcmimc2 *mimcap2 m4,mimcc/m4 nwell,pwell,space/w error a=area s=subs
 #endif (MIM)
 
  variants (orig)
@@ -6001,20 +6042,22 @@
  device resistor mrdp_hv   mvpdiffres *mvpdiff 
  device resistor xpwres   rpw    pwell
 
- device pdiode pdiode *pdiode nwell a=a p=p
- device ndiode ndiode *ndiode pwell,space/w a=a p=p
- device pdiode pdiode_h *mvpdiode nwell a=a p=p
- device ndiode ndiode_h *mvndiode pwell,space/w a=a p=p
+ device pdiode pdiode *pdiode nwell a=area
+ device ndiode ndiode *ndiode pwell,space/w a=area
+ device pdiode pdiode_h *mvpdiode nwell a=area
+ device ndiode ndiode_h *mvndiode pwell,space/w a=area
 
  # These are parasitic devices
- device ndiode ndiode_lvt *ndiodelvt pwell,space/w a=a p=p
- device pdiode pdiode_lvt *pdiodelvt nwell a=a p=p
- device pdiode pdiode_hvt *pdiodehvt nwell a=a p=p
- device ndiode ndiode_native *nndiode pwell,space/w a=a p=p
+ device ndiode ndiode_lvt *ndiodelvt pwell,space/w a=area
+ device pdiode pdiode_lvt *pdiodelvt nwell a=area
+ device pdiode pdiode_hvt *pdiodehvt nwell a=area
+ device ndiode ndiode_native *nndiode pwell,space/w a=area
 
- device subcircuit  pdiode_h *mvpdiode nwell a=a p=p
- device msubcircuit ndiode_h *mvndiode pwell,space/w a=a p=p
+ device bjt sky130_fd_pr__npn_05v0 npn dnwell *ndiff space/w error a1=area
+ device bjt sky130_fd_pr__pnp_05v0 pnp pwell,space/w *pdiff a1=area
 
+ device subcircuit  pdiode_h *mvpdiode nwell a=area
+ device msubcircuit ndiode_h *mvndiode pwell,space/w a=area
 
 #ifdef MIM
  device capacitor xcmimc1 *mimcap  *m3 1