diff --git a/.gitignore b/.gitignore
index cb5e29e1..bf299e04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ outputs
technology/freepdk45/ncsu_basekit
technology/sky130/*_lib
technology/sky130/tech/.magicrc
+technology/gf180mcu/*_lib
.idea
compiler/tests/results/
open_pdks/
@@ -21,6 +22,10 @@ openram.egg-info/
miniconda/
sky130A/
sky130B/
+gf180mcuA/
+gf180mcuB/
+gf180mcuC/
+gf180mcuD/
skywater-pdk/
sky130_fd_bd_sram/
docker/openram-ubuntu.log
diff --git a/Makefile b/Makefile
index dc07a5b1..fa6a450b 100644
--- a/Makefile
+++ b/Makefile
@@ -13,20 +13,28 @@ SRAM_LIB_GIT_REPO ?= https://github.com/vlsida/sky130_fd_bd_sram.git
# Use this for development
#SRAM_LIB_GIT_REPO ?= git@github.com:VLSIDA/sky130_fd_bd_sram.git
#SRAM_LIB_GIT_REPO ?= https://github.com/google/skywater-pdk-libs-sky130_fd_bd_sram.git
-SRAM_LIB_GIT_COMMIT ?= 9fcf3a78398037583b6d6c1ebac71957343c4bd8
+SRAM_LIB_GIT_COMMIT ?= dd64256961317205343a3fd446908b42bafba388
# Open PDKs
OPEN_PDKS_DIR ?= $(PDK_ROOT)/open_pdks
OPEN_PDKS_GIT_REPO ?= https://github.com/RTimothyEdwards/open_pdks.git
OPEN_PDKS_GIT_COMMIT ?= 1.0.311
+# Uncomment this for gf180 development
+# OPEN_PDKS_GIT_COMMIT ?= 1.0.395
#OPEN_PDKS_GIT_COMMIT ?= 7ea416610339d3c29af9d0d748ceadd3fd368608
SKY130_PDK ?= $(PDK_ROOT)/sky130A
+GF180_PDK ?= $(PDK_ROOT)/gf180
# Skywater PDK
SKY130_PDKS_DIR ?= $(PDK_ROOT)/skywater-pdk
SKY130_PDKS_GIT_REPO ?= https://github.com/google/skywater-pdk.git
SKY130_PDKS_GIT_COMMIT ?= f70d8ca46961ff92719d8870a18a076370b85f6c
+# GF180 PDK
+GF180_PDKS_DIR ?= $(PDK_ROOT)/gf180mcu-pdk
+GF180_PDKS_GIT_REPO ?= https://github.com/google/gf180mcu-pdk.git
+GF180_PDKS_GIT_COMMIT ?= main
+
# Create lists of all the files to copy/link
GDS_FILES := $(sort $(wildcard $(SRAM_LIB_DIR)/cells/*/*.gds))
GDS_FILES := $(GDS_FILES) $(PDK_ROOT)/skywater-pdk/libraries/sky130_fd_sc_hd/latest/cells/dlxtn/sky130_fd_sc_hd__dlxtn_1.gds
@@ -67,7 +75,17 @@ $(SKY130_PDKS_DIR): check-pdk-root
@git -C $(SKY130_PDKS_DIR) checkout $(SKY130_PDKS_GIT_COMMIT) && \
git -C $(SKY130_PDKS_DIR) submodule update --init libraries/sky130_fd_pr/latest libraries/sky130_fd_sc_hd/latest
-$(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR)
+$(GF180_PDKS_DIR): check-pdk-root
+ @echo "Cloning gf PDK..."
+ @[ -d $(PDK_ROOT)/gf180mcu-pdk ] || \
+ git clone https://github.com/google/gf180mcu-pdk.git $(PDK_ROOT)/gf180mcu-pdk
+ @cd $(GF180_PDKS_DIR) && \
+ git checkout main && git pull && \
+ git checkout -qf $(GF180_PDKS_GIT_COMMIT) && \
+ git submodule update --init libraries/gf180mcu_fd_pr/latest libraries/gf180mcu_fd_sc_mcu7t5v0/latest libraries/gf180mcu_fd_sc_mcu9t5v0/latest
+
+
+$(OPEN_PDKS_DIR): $(SKY130_PDKS_DIR) $(GF180_PDKS_DIR)
@echo "Cloning open_pdks..."
@[ -d $(OPEN_PDKS_DIR) ] || \
git clone $(OPEN_PDKS_GIT_REPO) $(OPEN_PDKS_DIR)
@@ -93,6 +111,28 @@ else
conda deactivate
endif
+$(GF180_PDK): $(OPEN_PDKS_DIR) $(GF180_PDKS_DIR)
+ @echo "Installing open_pdks..."
+ifeq ($(CONDA_DIR),"")
+ @cd $(PDK_ROOT)/open_pdks && \
+ ./configure --enable-gf180mcu-pdk=$(PDK_ROOT)/gf180mcu-pdk/libraries --enable-primitive-gf180mcu=$(GF180_PDKS_DIR)/libraries/gf180mcu_fd_pr/latest \
+ --enable-sc-7t5v0-gf180mcu=$(GF180_PDKS_DIR)/libraries/gf180mcu_fd_sc_mcu7t5v0/latest --enable-sc-9t5v0-gf180mcu=$(GF180_PDKS_DIR)/libraries/gf180mcu_fd_sc_mcu9t5v0/latest && \
+ cd gf180mcu && \
+ make veryclean && \
+ make && \
+ make SHARED_PDKS_PATH=$(PDK_ROOT) install
+else
+ @source $(TOP_DIR)/miniconda/bin/activate && \
+ cd $(PDK_ROOT)/open_pdks && \
+ ./configure --enable-gf180mcu-pdk=$(PDK_ROOT)/gf180mcu-pdk/libraries --enable-primitive-gf180mcu=$(GF180_PDKS_DIR)/libraries/gf180mcu_fd_pr/latest \
+ --enable-sc-7t5v0-gf180mcu=$(GF180_PDKS_DIR)/libraries/gf180mcu_fd_sc_mcu7t5v0/latest --enable-sc-9t5v0-gf180mcu=$(GF180_PDKS_DIR)/libraries/gf180mcu_fd_sc_mcu9t5v0/latest && \
+ cd gf180mcu && \
+ make veryclean && \
+ make && \
+ make SHARED_PDKS_PATH=$(PDK_ROOT) install && \
+ conda deactivate
+endif
+
$(SRAM_LIB_DIR): check-pdk-root
@echo "Cloning SRAM library..."
@[ -d $(SRAM_LIB_DIR) ] || \
@@ -116,6 +156,10 @@ pdk: $(SKY130_PDK)
@true
.PHONY: pdk
+gf180-pdk: $(GF180_PDK)
+ @true
+.PHONY: gf180-pdk
+
$(INSTALL_BASE)/gds_lib: $(GDS_FILES)
@echo
@echo "Setting up GDS cell library for OpenRAM."
diff --git a/README.md b/README.md
index 45d8edae..244e41fc 100644
--- a/README.md
+++ b/README.md
@@ -66,12 +66,12 @@ OpenRAM is licensed under the [BSD 3-Clause License](./LICENSE).
+ [H. Nichols, "Statistical Modeling of SRAMs," M.S. Thesis, UCSC, 2022.](https://escholarship.org/content/qt7vx9n089/qt7vx9n089_noSplash_cfc4ba479d8eb1b6ec25d7c92357bc18.pdf?t=ra9wzr)
-
+
# Contributors & Acknowledgment
- [Matthew Guthaus] from [VLSIDA] created the OpenRAM project and is the lead architect.
- [James Stine] from [VLSIARCH] co-founded the project.
-- Many students: Hunter Nichols, Michael Grimes, Jennifer Sowash, Yusu Wang, Joey Kunzler, Jesse Cirimelli-Low, Samira Ataei, Bin Wu, Brian Chen, Jeff Butera
+- Many students: Hunter Nichols, Michael Grimes, Jennifer Sowash, Yusu Wang, Joey Kunzler, Jesse Cirimelli-Low, Samira Ataei, Bin Wu, Brian Chen, Jeff Butera, Sage Walker
If I forgot to add you, please let me know!
diff --git a/VERSION b/VERSION
index a39cbb3c..8d4450b7 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.40
+1.2.42
diff --git a/compiler/base/contact.py b/compiler/base/contact.py
index 84f60d0e..7ca08d7d 100644
--- a/compiler/base/contact.py
+++ b/compiler/base/contact.py
@@ -10,7 +10,7 @@ from openram.tech import drc, layer, preferred_directions
from openram.tech import layer as tech_layers
from .hierarchy_design import hierarchy_design
from .vector import vector
-
+from .utils import ceil
class contact(hierarchy_design):
"""
@@ -41,7 +41,7 @@ class contact(hierarchy_design):
self.add_comment("implant type: {}\n".format(implant_type))
self.add_comment("well_type: {}\n".format(well_type))
- self.is_well_contact = implant_type == well_type
+ self.is_well_contact = (implant_type == well_type) and implant_type is not None
# If we have a special tap layer, use it
self.layer_stack = layer_stack
@@ -125,6 +125,8 @@ class contact(hierarchy_design):
self.first_layer_minwidth = drc("minwidth_{0}".format(self.first_layer_name))
self.first_layer_enclosure = drc("{0}_enclose_{1}".format(self.first_layer_name, self.via_layer_name))
+ self.first_layer_minarea = drc("minarea_{0}".format(self.first_layer_name))
+
# If there's a different rule for active
# FIXME: Make this more elegant
if self.is_well_contact and self.first_layer_name == "active" and "tap_extend_contact" in drc.keys():
@@ -135,7 +137,7 @@ class contact(hierarchy_design):
self.second_layer_minwidth = drc("minwidth_{0}".format(self.second_layer_name))
self.second_layer_enclosure = drc("{0}_enclose_{1}".format(self.second_layer_name, self.via_layer_name))
self.second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name))
-
+ self.second_layer_minarea = drc("minarea_{0}".format(self.second_layer_name))
# In some technologies, the minimum width may be larger
# than the overlap requirement around the via, so
# check this for each dimension.
@@ -143,7 +145,7 @@ class contact(hierarchy_design):
self.first_layer_horizontal_enclosure = max(self.first_layer_enclosure,
(self.first_layer_minwidth - self.contact_array_width) / 2)
self.first_layer_vertical_enclosure = max(self.first_layer_extend,
- (self.first_layer_minwidth - self.contact_array_height) / 2)
+ (self.first_layer_minwidth - self.contact_array_height) / 2)
elif self.directions[0] == "H":
self.first_layer_horizontal_enclosure = max(self.first_layer_extend,
(self.first_layer_minwidth - self.contact_array_width) / 2)
@@ -166,7 +168,7 @@ class contact(hierarchy_design):
self.second_layer_vertical_enclosure = max(self.second_layer_enclosure,
(self.second_layer_minwidth - self.contact_array_width) / 2)
else:
- debug.error("Invalid secon layer direction: ".format(self.directions[1]), -1)
+ debug.error("Invalid second layer direction: ".format(self.directions[1]), -1)
def create_contact_array(self):
""" Create the contact array at the origin"""
@@ -221,6 +223,18 @@ class contact(hierarchy_design):
first_layer_name = "tap"
else:
first_layer_name = self.first_layer_name
+
+ area = self.first_layer_width * self.first_layer_height
+ if area < self.first_layer_minarea and self.is_well_contact:
+ if self.directions[0] == "V":
+ area_extend = (self.first_layer_minarea / self.first_layer_width) - self.first_layer_height
+ self.first_layer_height = ceil(self.first_layer_height + area_extend)
+ self.first_layer_position = self.first_layer_position - vector(0, area_extend / 2)
+ elif self.directions[0] == "H":
+ area_extend = (self.first_layer_minarea / self.first_layer_height) - self.first_layer_width
+ self.first_layer_width = ceil(self.first_layer_height + area_extend)
+ self.first_layer_position = self.first_layer_position - vector(area_extend / 2, 0)
+
self.add_rect(layer=first_layer_name,
offset=self.first_layer_position,
width=self.first_layer_width,
@@ -236,6 +250,7 @@ class contact(hierarchy_design):
self.second_layer_minwidth)
self.second_layer_height = max(self.contact_array_height + 2 * self.second_layer_vertical_enclosure,
self.second_layer_minwidth)
+
self.add_rect(layer=self.second_layer_name,
offset=self.second_layer_position,
width=self.second_layer_width,
diff --git a/compiler/base/hierarchy_layout.py b/compiler/base/hierarchy_layout.py
index f9d3e889..05b8603a 100644
--- a/compiler/base/hierarchy_layout.py
+++ b/compiler/base/hierarchy_layout.py
@@ -22,7 +22,7 @@ from openram.sram_factory import factory
from openram import OPTS
from .vector import vector
from .pin_layout import pin_layout
-from .utils import round_to_grid
+from .utils import round_to_grid, ceil
from . import geometry
try:
@@ -187,7 +187,7 @@ class layout():
pass
# Skip computing the pitch for non-routing layers
- if layer_id in ["active", "nwell"]:
+ if layer_id in ["active", "nwell", "pwell"]:
continue
# Add the pitch
@@ -259,8 +259,7 @@ class layout():
contact_width = contact1.first_layer_width
layer_space = getattr(layout, layer1 + "_space")
- #print(layer_stack)
- #print(contact1)
+
pitch = contact_width + layer_space
return round_to_grid(pitch)
@@ -838,8 +837,7 @@ class layout():
from_id = tech_layer_indices[from_layer]
to_id = tech_layer_indices[to_layer]
-
- layer_list = [x for x in tech_layer_indices.keys() if tech_layer_indices[x] >= from_id and tech_layer_indices[x] < to_id]
+ layer_list = [x for x in tech_layer_indices.keys() if tech_layer_indices[x] > from_id and tech_layer_indices[x] < to_id]
return layer_list
@@ -944,14 +942,19 @@ class layout():
return (bot_rect, top_rect)
- def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True):
+ def route_horizontal_pins(self, name, insts=None, layer=None, xside="cx", yside="cy", full_width=True, new_name=None):
"""
Route together all of the pins of a given name that horizontally align.
Uses local_insts if insts not specified.
Uses center of pin by default, or top or botom if specified.
+ New top level pin can be renamed with new_name, otherwise the new pin will keep the same name as old pins
TODO: Add equally spaced option for IR drop min, right now just 2
"""
+ if new_name is not None:
+ pin_name = new_name
+ else:
+ pin_name = name
bins = {}
if not insts:
@@ -1011,16 +1014,17 @@ class layout():
left_pos = vector(left_x + 0.5 * via_width, y)
right_pos = vector(right_x + 0.5 * via_width, y)
-# self.add_layout_pin_rect_ends(name=name,
-# layer=pin_layer,
-# start=left_pos,
-# end=right_pos,
-# width=via_height)
- self.add_layout_pin_segment_center(text=name,
- layer=pin_layer,
- start=left_pos,
- end=right_pos,
- width=via_height)
+ # self.add_layout_pin_rect_ends(name=name,
+ # layer=pin_layer,
+ # start=left_pos,
+ # end=right_pos,
+ # width=via_height)
+
+ self.add_layout_pin_segment_center(text=pin_name,
+ layer=pin_layer,
+ start=left_pos,
+ end=right_pos,
+ width=via_height)
def add_layout_end_pin_segment_center(self, text, layer, start, end):
"""
@@ -1368,12 +1372,11 @@ class layout():
min_width = drc("minwidth_{}".format(layer))
if preferred_directions[layer] == "V":
- new_height = max(min_area / width, min_width)
+ new_height = ceil(max(min_area / width, min_width))
new_width = width
else:
- new_width = max(min_area / height, min_width)
+ new_width = ceil(max(min_area / height, min_width))
new_height = height
-
debug.check(min_area <= round_to_grid(new_height*new_width), "Min area violated.")
self.add_rect_center(layer=layer,
diff --git a/compiler/modules/pgate.py b/compiler/modules/pgate.py
index 8f21819a..1cfed389 100644
--- a/compiler/modules/pgate.py
+++ b/compiler/modules/pgate.py
@@ -48,6 +48,11 @@ class pgate(design):
# This is the space from a S/D contact to the supply rail
contact_to_vdd_rail_space = 0.5 * self.route_layer_width + self.route_layer_space
+
+ # This is a result of the m1 extend contact drc rule being really long in comparison to sky130.
+ # Currently the "extend" drc rule acts to fulfil minimum metal areas on contacts and isnt reflective of an actual drc rule
+ if OPTS.tech_name == "gf180mcu":
+ contact_to_vdd_rail_space += 0.5 * self.route_layer_space
# This is a poly-to-poly of a flipped cell
poly_to_poly_gate_space = self.poly_extend_active + 0.5 * self.poly_space
diff --git a/compiler/modules/pinv_dec.py b/compiler/modules/pinv_dec.py
index 1ee75266..a1008c0d 100644
--- a/compiler/modules/pinv_dec.py
+++ b/compiler/modules/pinv_dec.py
@@ -187,7 +187,11 @@ class pinv_dec(pinv):
# Pick point at right most of NMOS and connect over to PMOS
nmos_drain_pos = nmos_drain_pin.lc()
- right_side = vector(self.width, nmos_drain_pos.y)
+
+ if self.flip_io:
+ right_side = vector(self.pmos_inst.get_pin("D").cx(), nmos_drain_pos.y)
+ else:
+ right_side = vector(self.width, nmos_drain_pos.y)
self.add_layout_pin_segment_center("Z",
self.route_layer,
diff --git a/compiler/modules/ptx.py b/compiler/modules/ptx.py
index 250cd3f6..5645d3d7 100644
--- a/compiler/modules/ptx.py
+++ b/compiler/modules/ptx.py
@@ -129,12 +129,14 @@ class ptx(design):
# be decided in the layout later.
area_sd = 2.5 * self.poly_width * self.tx_width
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
+
+ self.channel_length = drc("minlength_channel")
if cell_props.ptx.model_is_subckt:
# sky130
main_str = "X{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
self.mults,
self.tx_width,
- drc("minwidth_poly"))
+ self.channel_length)
# Perimeters are in microns
# Area is in u since it is microns square
area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd,
@@ -143,7 +145,7 @@ class ptx(design):
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
- drc("minwidth_poly"))
+ self.channel_length)
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
self.spice_device = main_str + area_str
@@ -160,17 +162,17 @@ class ptx(design):
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult=1".format("nshort" if self.tx_type == "nmos" else "pshort",
self.mults,
self.tx_width,
- drc("minwidth_poly"))
+ self.channel_length)
elif cell_props.ptx.model_is_subckt:
self.lvs_device = "X{{0}} {{1}} {0} m={1} w={2}u l={3}u".format(spice[self.tx_type],
self.mults,
self.tx_width,
- drc("minwidth_poly"))
+ self.channel_length)
else:
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
- drc("minwidth_poly"))
+ self.channel_length)
def setup_layout_constants(self):
"""
@@ -216,11 +218,11 @@ class ptx(design):
self.active_width = 2 * self.end_to_contact + self.active_contact.width \
+ 2 * self.active_contact_to_gate + self.poly_width + (self.mults - 1) * self.poly_pitch
- # Active height is just the transistor width
+ # Active height is either the transistor width or the wide enough to enclose the active contact
self.active_height = self.tx_width
-
# Poly height must include poly extension over active
- self.poly_height = self.tx_width + 2 * self.poly_extend_active
+ self.poly_height = self.active_height + 2 * self.poly_extend_active
+
self.active_offset = vector([self.well_enclose_active] * 2)
diff --git a/compiler/modules/rom_address_control_array.py b/compiler/modules/rom_address_control_array.py
index 9f59ca71..a7e45fe6 100644
--- a/compiler/modules/rom_address_control_array.py
+++ b/compiler/modules/rom_address_control_array.py
@@ -21,11 +21,11 @@ class rom_address_control_array(design):
self.size=inv_size
self.cols = cols
self.route_layer = route_layer
- dff = factory.create(module_type="dff")
if name=="":
name = "rom_inv_array_{0}".format(cols)
if inv_height == None:
- self.inv_height = dff.height * 0.5
+
+ self.inv_height = drc("minwidth_{}".format(route_layer)) * 14
else:
self.inv_height = inv_height
@@ -34,6 +34,7 @@ class rom_address_control_array(design):
self.inv_layer = "li"
else:
self.inv_layer = "m1"
+ self.route_layer = "m2"
super().__init__(name)
self.create_netlist()
self.create_layout()
@@ -118,4 +119,15 @@ class rom_address_control_array(design):
for pin in tmp_pins:
self.copy_layout_pin(self, "vdd_edge", "vdd")
- self.remove_layout_pin("vdd_edge")
\ No newline at end of file
+ self.remove_layout_pin("vdd_edge")
+
+ tmp_pins = []
+ for pin in self.get_pins("gnd"):
+ edge = vector(pin.rx() + 0.5 * self.route_width, pin.cy())
+ tmp_pins.append(self.add_layout_pin_rect_center("gnd_edge", layer=self.route_layer, offset=edge))
+ self.copy_layout_pin_shapes("gnd")
+ self.remove_layout_pin("gnd")
+
+ for pin in tmp_pins:
+ self.copy_layout_pin(self, "gnd_edge", "gnd")
+ self.remove_layout_pin("gnd_edge")
\ No newline at end of file
diff --git a/compiler/modules/rom_address_control_buf.py b/compiler/modules/rom_address_control_buf.py
index 9e092ff0..d3b1dd47 100644
--- a/compiler/modules/rom_address_control_buf.py
+++ b/compiler/modules/rom_address_control_buf.py
@@ -10,6 +10,8 @@ from openram.base import design
from openram.sram_factory import factory
from openram.base import vector
from openram.tech import layer, drc
+from openram import OPTS
+
@@ -25,8 +27,11 @@ class rom_address_control_buf(design):
self.size = size
if "li" in layer:
self.inv_layer = "li"
+ self.non_inverting_layer = "m2"
else:
self.inv_layer = "m1"
+ self.route_layer = "m2"
+ self.non_inverting_layer = "m3"
super().__init__(name)
self.create_netlist()
self.create_layout()
@@ -47,11 +52,11 @@ class rom_address_control_buf(design):
def create_modules(self):
- self.inv = factory.create(module_type="pinv_dec", module_name="inv_array_mod", add_wells=False, size=self.size)
- self.nand = factory.create(module_type="nand2_dec", height=self.inv.height)
# For layout constants
self.cell = factory.create(module_type="rom_base_cell")
+ self.nand = factory.create(module_type="nand2_dec")
+ self.inv = factory.create(module_type="pinv_dec", module_name="inv_array_mod", add_wells=False, size=self.size, height=self.nand.height)
def add_pins(self):
self.add_pin("A_in", "INPUT")
@@ -108,16 +113,23 @@ class rom_address_control_buf(design):
Aint_in = self.addr_bar_nand.get_pin("B")
A_in = self.inv_inst.get_pin("A")
-
+ vdd_rail = self.addr_nand.get_pin("vdd")
# Find the center of the pmos poly/gate
poly_right = clk1_pin.cx() + self.poly_enclose_contact + 0.5 * self.contact_width
ppoly_center = poly_right - 0.7 * self.poly_width
+ poly_y = A_out.cy()
+
+ # Placement of gate contacts for NAND cell are different in gf180 which requires tech-specific placement.
+ if OPTS.tech_name == "gf180mcu":
+ poly_y = vdd_rail.cy() + 0.5 * drc("minwidth_tx") * 3 + self.poly_extend_active
+ ppoly_center = A_out.cx() + 0.5 * self.interconnect_width + 0.5 * self.poly_width
+
contact_offset = vector(ppoly_center, clk2_pin.cy())
# Route the two shared clk inputs together by connecting poly
- self.add_segment_center("poly", contact_offset, vector(ppoly_center, A_out.cy()))
+ self.add_segment_center("poly", contact_offset, vector(ppoly_center, poly_y))
clk_offset = vector(clk2_pin.cx(), self.addr_nand.uy())
@@ -129,18 +141,18 @@ class rom_address_control_buf(design):
# Route first NAND output to second NAND input
start = A_out.center()
end = Aint_in.center()
- self.add_path("m2", [start, end])
- self.add_via_stack_center(Aint_in.center(), self.inv_layer, "m2")
- self.add_via_stack_center(A_out.center(), self.inv_layer, "m2")
+ self.add_path(self.non_inverting_layer, [start, end])
+ self.add_via_stack_center(Aint_in.center(), self.inv_layer, self.non_inverting_layer)
+ self.add_via_stack_center(A_out.center(), self.inv_layer, self.non_inverting_layer)
# Route first NAND to output pin
- self.add_segment_center("m2", end, vector(end.x, self.addr_bar_nand.uy()))
- self.add_layout_pin_rect_center("A_out", offset=vector(end.x, self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2")
+ self.add_segment_center(self.non_inverting_layer, end, vector(end.x, self.addr_bar_nand.uy()))
+ self.add_layout_pin_rect_center("A_out", offset=vector(end.x, self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer=self.non_inverting_layer)
# Route second NAND to output pin
- self.add_via_stack_center(Abar_out.center(), self.inv_layer, "m2")
- self.add_segment_center("m2", Abar_out.center(), vector(Abar_out.cx(), self.addr_bar_nand.uy()))
- self.add_layout_pin_rect_center("Abar_out", offset=vector(Abar_out.cx(), self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer="m2")
+ self.add_via_stack_center(Abar_out.center(), self.inv_layer, self.non_inverting_layer)
+ self.add_segment_center(self.non_inverting_layer, Abar_out.center(), vector(Abar_out.cx(), self.addr_bar_nand.uy()))
+ self.add_layout_pin_rect_center("Abar_out", offset=vector(Abar_out.cx(), self.addr_bar_nand.uy() - 0.5 * self.m2_width), layer=self.non_inverting_layer)
# Route inverter output to NAND
end = vector(Abar_int_out.cx(), Abar_in.cy() + 0.5 * self.interconnect_width)
@@ -166,7 +178,6 @@ class rom_address_control_buf(design):
left_edge = self.inv_inst.get_pin("Z").cx() - 2 * self.contact_width - 2 * self.active_contact_to_gate - 4 * self.active_enclose_contact - self.poly_width - self.active_space
contact_pos = vector(left_edge, source_pin.cy())
-
self.add_via_center(layers=self.active_stack,
offset=contact_pos,
implant_type="n",
diff --git a/compiler/modules/rom_bank.py b/compiler/modules/rom_bank.py
index 244cbe16..45a7ad81 100644
--- a/compiler/modules/rom_bank.py
+++ b/compiler/modules/rom_bank.py
@@ -113,8 +113,7 @@ class rom_bank(design,rom_verilog):
# FIXME: Somehow ROM layout behaves weird and doesn't add all the pin
# shapes before routing supplies
init_bbox = self.get_bbox()
- if OPTS.route_supplies:
- self.route_supplies(init_bbox)
+ self.route_supplies(init_bbox)
# Route the pins to the perimeter
if OPTS.perimeter_pins:
# We now route the escape routes far enough out so that they will
@@ -297,7 +296,7 @@ class rom_bank(design,rom_verilog):
def place_bitline_inverter(self):
self.bitline_inv_inst.place(offset=[0,0], rotate=90)
- inv_y_offset = self.array_inst.by() - self.bitline_inv_inst.width - 2 * self.m1_pitch
+ inv_y_offset = self.array_inst.by() - self.bitline_inv_inst.width - 1.5 * self.m1_pitch
inv_x_offset = self.array_inst.get_pin("bl_0_0").cx() - self.bitline_inv_inst.get_pin("out_0").cx()
self.inv_offset = vector(inv_x_offset, inv_y_offset)
@@ -368,8 +367,11 @@ class rom_bank(design,rom_verilog):
# Route precharge to col decoder
start = prechrg_control.center()
- mid1 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, prechrg_control.cy())
- mid2 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, col_decode_prechrg.cy())
+
+ path_x = self.control_inst.rx() + 1.2 * self.route_layer_pitch
+
+ mid1 = vector(path_x, prechrg_control.cy())
+ mid2 = vector(path_x, col_decode_prechrg.cy())
end = col_decode_prechrg.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
@@ -378,7 +380,7 @@ class rom_bank(design,rom_verilog):
offset=end)
start = mid1
- mid1 = vector(self.control_inst.rx() + self.interconnect_layer_pitch, start.y)
+ mid1 = vector(path_x, start.y)
mid2 = vector(mid1.x, col_decode_clk.cy())
end = col_decode_clk.center()
self.add_path(self.route_stack[0], [start, mid1, mid2, end])
@@ -388,6 +390,10 @@ class rom_bank(design,rom_verilog):
mid = vector(col_decode_prechrg.cx(), array_prechrg.cy() )
self.add_path(self.route_stack[0], [array_prechrg.center(), mid, col_decode_prechrg.center()])
+ self.add_via_stack_center(from_layer=self.route_stack[0],
+ to_layer=array_prechrg.layer,
+ offset=array_prechrg.center())
+
def route_clock(self):
clk_out = self.control_inst.get_pin("clk_out")
@@ -409,6 +415,10 @@ class rom_bank(design,rom_verilog):
to_layer=row_decode_clk.layer,
offset=addr_control_clk)
+ self.add_via_stack_center(from_layer=self.route_stack[2],
+ to_layer=row_decode_prechrg.layer,
+ offset=row_decode_prechrg.center())
+
self.add_segment_center(row_decode_clk.layer, addr_control_clk, row_decode_clk.rc())
def route_array_outputs(self):
@@ -417,7 +427,11 @@ class rom_bank(design,rom_verilog):
inv_out_pins = [self.bitline_inv_inst.get_pin("out_{}".format(bl)) for bl in range(self.cols)]
mux_pins = [self.mux_inst.get_pin("bl_{}".format(bl)) for bl in range(self.cols)]
- self.connect_col_pins(self.interconnect_layer, array_out_pins + inv_in_pins, round=True, directions="nonpref")
+ if "li" in layer:
+ output_layer = "m1"
+ else:
+ output_layer = "m3"
+ self.connect_col_pins(output_layer, array_out_pins + inv_in_pins, round=True, directions="nonpref")
self.connect_col_pins(self.interconnect_layer, inv_out_pins + mux_pins, round=True, directions="nonpref")
def route_output_buffers(self):
@@ -450,34 +464,36 @@ class rom_bank(design,rom_verilog):
for inst in self.insts:
self.copy_power_pins(inst, pin_name)
- from openram.router import supply_router as router
- rtr = router(layers=self.supply_stack,
- design=self,
- bbox=bbox,
- pin_type=OPTS.supply_pin_type)
- rtr.route()
+ if OPTS.route_supplies:
- if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
- # Find the lowest leftest pin for vdd and gnd
- for pin_name in ["vdd", "gnd"]:
- # Copy the pin shape(s) to rectangles
- for pin in self.get_pins(pin_name):
- self.add_rect(layer=pin.layer,
- offset=pin.ll(),
- width=pin.width(),
- height=pin.height())
+ from openram.router import supply_router as router
+ rtr = router(layers=self.supply_stack,
+ design=self,
+ bbox=bbox,
+ pin_type=OPTS.supply_pin_type)
+ rtr.route()
- # Remove the pin shape(s)
- self.remove_layout_pin(pin_name)
+ if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
+ # Find the lowest leftest pin for vdd and gnd
+ for pin_name in ["vdd", "gnd"]:
+ # Copy the pin shape(s) to rectangles
+ for pin in self.get_pins(pin_name):
+ self.add_rect(layer=pin.layer,
+ offset=pin.ll(),
+ width=pin.width(),
+ height=pin.height())
- # Get new pins
- pins = rtr.get_new_pins(pin_name)
- for pin in pins:
- self.add_layout_pin(pin_name,
- pin.layer,
- pin.ll(),
- pin.width(),
- pin.height())
+ # Remove the pin shape(s)
+ self.remove_layout_pin(pin_name)
+
+ # Get new pins
+ pins = rtr.get_new_pins(pin_name)
+ for pin in pins:
+ self.add_layout_pin(pin_name,
+ pin.layer,
+ pin.ll(),
+ pin.width(),
+ pin.height())
def route_escape_pins(self, bbox):
pins_to_route = []
diff --git a/compiler/modules/rom_base_array.py b/compiler/modules/rom_base_array.py
index 9c380624..3a0848ee 100644
--- a/compiler/modules/rom_base_array.py
+++ b/compiler/modules/rom_base_array.py
@@ -10,7 +10,7 @@
import math
from .bitcell_base_array import bitcell_base_array
from openram.base import vector
-from openram import OPTS, debug
+from openram import debug
from openram.sram_factory import factory
from openram.tech import drc, layer
@@ -22,7 +22,9 @@ class rom_base_array(bitcell_base_array):
self.data = bitmap
self.tap_direction = tap_direction
- self.pitch_match = pitch_match
+ # This attribute is redundant with the tap direction
+ # TODO: consolidate pitch matching logic to just be based on tap direction
+ self.pitch_match = tap_direction == "row"
self.bitline_layer = bitline_layer
self.strap_spacing = strap_spacing
self.wordline_layer = wordline_layer
@@ -88,6 +90,7 @@ class rom_base_array(bitcell_base_array):
else:
self.poly_tap = factory.create(module_type="rom_poly_tap", add_active_tap=True)
self.end_poly_tap = factory.create(module_type="rom_poly_tap", place_poly=True)
+
self.precharge_array = factory.create(module_type="rom_precharge_array",
cols=self.column_size,
strap_spacing=self.strap_spacing,
@@ -156,7 +159,7 @@ class rom_base_array(bitcell_base_array):
name = "bit_r{0}_c{1}".format(row, col)
# when col = 0, bl_h is connected to precharge, otherwise connect to previous bl connection
- # when col = col_size - 1 connected column_sizeto gnd otherwise create new bl connection
+ # when col = col_size - 1 connected to gnd otherwise create new bl connection
# debug.info(1, "Create cell: r{0}, c{1}".format(row, col))
if row == self.row_size:
@@ -202,8 +205,8 @@ class rom_base_array(bitcell_base_array):
pitch = drc["{0}_to_{0}".format(self.wordline_layer)]
drain_l = self.cell_list[self.row_size][0].get_pin("D")
drain_r = self.cell_list[self.row_size][self.column_size - 1].get_pin("D")
- gnd_l = drain_l.center() + vector(-0.5 * self.route_width, pitch + via_width + self.route_pitch)
- gnd_r = drain_r.center() + vector(0.5 * self.route_width, pitch + via_width + self.route_pitch)
+ gnd_l = drain_l.center() + vector(-0.5 * self.route_width, 0.5 * pitch + via_width + self.route_pitch)
+ gnd_r = drain_r.center() + vector(0.5 * self.route_width, 0.5 * pitch + via_width + self.route_pitch)
self.add_layout_pin_rect_ends(name="gnd", layer=self.bitline_layer, start=gnd_l, end=gnd_r)
@@ -215,7 +218,7 @@ class rom_base_array(bitcell_base_array):
self.remove_layout_pin("gnd")
active_tap_pins = [self.active_tap_list[i].get_pin("active_tap") for i in range(len(self.active_tap_list))]
- self.connect_col_pins(layer=self.supply_stack[0], pins=active_tap_pins, name="gnd_tmp")
+ self.connect_col_pins(layer=self.supply_stack[0], pins=active_tap_pins, name="gnd_tmp", directions="nonpref")
gnd_y = gnd_l.y
min_x = float('inf')
@@ -231,12 +234,10 @@ class rom_base_array(bitcell_base_array):
bottom = vector(pin.cx(), pin.by())
top = vector(pin.cx(), gnd_y)
self.add_via_stack_center(offset=top, from_layer=self.bitline_layer, to_layer=self.supply_stack[0])
- self.add_via_center(offset=bottom, layers=self.supply_stack)
self.add_layout_pin_rect_ends(name="gnd", layer=self.supply_stack[0], start=bottom, end=top)
self.remove_layout_pin("gnd_tmp")
- self.add_segment_center(layer=self.supply_stack[2], start=vector(min_x, bottom.y), end=vector(max_x, bottom.y))
self.add_segment_center(layer=self.bitline_layer, start=gnd_l, end=vector(min_x, gnd_l.y))
self.add_segment_center(layer=self.bitline_layer, start=gnd_r, end=vector(max_x, gnd_r.y))
@@ -246,7 +247,6 @@ class rom_base_array(bitcell_base_array):
self.cell_pos = {}
self.strap_pos = {}
pitch_offset = 0
-
for row in range(self.row_size + 1):
if row % self.tap_spacing == 0 and self.pitch_match and row != self.row_size:
@@ -310,7 +310,7 @@ class rom_base_array(bitcell_base_array):
directions="nonpref")
self.add_via_stack_center(offset=tap_pos,
from_layer=self.active_stack[2],
- to_layer=self.wordline_layer)
+ to_layer=self.wordline_layer, directions="nonpref")
self.gnd_taps.append(self.add_layout_pin_rect_center("gnd_tap", self.wordline_layer, tap_pos))
def place_precharge(self):
@@ -326,8 +326,11 @@ class rom_base_array(bitcell_base_array):
def place_bitline_contacts(self):
+ if "li" in layer:
+ output_layer = "m1"
+ else:
+ output_layer = "m3"
rail_y = self.precharge_inst.get_pins("vdd")[0].cy()
-
for bl in range(self.column_size):
src_pin = self.cell_list[0][bl].get_pin("S")
@@ -340,9 +343,12 @@ class rom_base_array(bitcell_base_array):
output_pos = vector(corrected.x, rail_y)
- self.add_segment_center(self.bitline_layer, corrected, output_pos)
+ if output_layer != self.bitline_layer:
+ self.add_via_stack_center(from_layer=self.bitline_layer, to_layer=output_layer, offset=corrected)
- self.add_layout_pin_rect_center(self.bitline_names[0][bl], self.bitline_layer, output_pos )
+ self.add_segment_center(output_layer, corrected, output_pos)
+
+ self.add_layout_pin_rect_center(self.bitline_names[0][bl], output_layer, output_pos )
def route_precharge(self):
for bl in range(self.column_size):
@@ -375,3 +381,5 @@ class rom_base_array(bitcell_base_array):
poly_tap_pins = [self.poly_tap_list[i].get_pin("poly_tap") for i in range(len(self.poly_tap_list))]
self.connect_row_pins(layer=self.wordline_layer, pins=poly_tap_pins)
+ self.connect_row_pins(layer="poly", pins=poly_tap_pins)
+
diff --git a/compiler/modules/rom_base_cell.py b/compiler/modules/rom_base_cell.py
index 28fc07f0..da57ff23 100644
--- a/compiler/modules/rom_base_cell.py
+++ b/compiler/modules/rom_base_cell.py
@@ -15,10 +15,16 @@ from openram.tech import drc
class rom_base_cell(design):
- def __init__(self, name="", bitline_layer="li", bit_value=1, add_well=False):
+ def __init__(self, name="", bitline_layer=None, bit_value=1, add_well=False):
super().__init__(name)
self.bit_value = bit_value
- self.bitline_layer = bitline_layer
+
+ if bitline_layer is None and OPTS.tech_name == "sky130":
+ self.bitline_layer = "li"
+ elif bitline_layer is None:
+ self.bitline_layer = "m1"
+ else:
+ self.bitline_layer = bitline_layer
self.add_well=add_well
self.create_netlist()
self.create_layout()
@@ -41,6 +47,7 @@ class rom_base_cell(design):
# Calculates offsets of cell width and height so that tiling of cells does not violate any drc rules
def setup_drc_offsets(self):
+ self.bitline_width = drc(f"minwidth_{self.bitline_layer}")
self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active)
def add_boundary(self):
@@ -50,9 +57,7 @@ class rom_base_cell(design):
#cell width with offsets applied, height becomes width when the cells are rotated
width = self.cell_inst.height + 2 * self.poly_extend_active
-
# make the cells square so the pitch of wordlines will match bitlines
-
if width > height:
self.width = width
self.height = width
@@ -62,7 +67,6 @@ class rom_base_cell(design):
super().add_boundary()
-
def add_modules(self):
self.nmos = factory.create(module_type="ptx",
@@ -72,17 +76,16 @@ class rom_base_cell(design):
add_drain_contact=self.bitline_layer
)
-
def create_tx(self):
self.cell_inst = self.add_inst( name=self.name + "_nmos",
mod=self.nmos,
)
+
if self.bit_value == 0:
self.connect_inst(["bl", "wl", "bl", "gnd"])
else:
self.connect_inst(["bl_h", "wl", "bl_l", "gnd"])
-
def add_pins(self):
if self.bit_value == 0 :
pin_list = ["bl", "wl", "gnd"]
@@ -95,15 +98,14 @@ class rom_base_cell(design):
def place_tx(self):
- # sizing_offset = self.cell_inst.height - drc["minwidth_tx"]
tx_offset = vector(self.poly_extend_active + self.cell_inst.height + self.poly_size,- 0.5 * self.contact_width - self.active_enclose_contact)
- # add rect of poly to account for offset from drc spacing
- # self.add_rect_center("poly", poly_offset, self.poly_extend_active_spacing, self.poly_width)
self.cell_inst.place(tx_offset, rotate=90)
self.copy_layout_pin(self.cell_inst, "S", "S")
self.copy_layout_pin(self.cell_inst, "D", "D")
+ self.copy_layout_pin(self.cell_inst, "G", "G")
+
self.source_pos = self.cell_inst.get_pin("S").center()
def place_poly(self):
@@ -113,13 +115,10 @@ class rom_base_cell(design):
end = poly_offset + vector(self.poly_size, 0)
self.add_segment_center("poly", start, end)
-
def place_bitline(self):
-
start = self.get_pin("D").center()
- end = start + vector(0, 2 * self.active_enclose_contact + 0.5 * self.contact_width + self.active_space)
- self.add_segment_center(self.bitline_layer, start, end)
+ end = start + vector(0, 2 * self.active_enclose_contact + self.contact_width + self.active_space)
+ self.add_segment_center(self.bitline_layer, start, end, self.bitline_width * 2)
def short_gate(self):
-
self.add_segment_center(self.bitline_layer, self.get_pin("D").center(), self.get_pin("S").center())
\ No newline at end of file
diff --git a/compiler/modules/rom_decoder.py b/compiler/modules/rom_decoder.py
index 760af8c4..7d6dd928 100644
--- a/compiler/modules/rom_decoder.py
+++ b/compiler/modules/rom_decoder.py
@@ -9,15 +9,19 @@ from math import ceil, log
from openram.sram_factory import factory
from openram.base import vector, design
from openram import OPTS
-from openram.tech import drc
-
+from openram.tech import drc, layer
class rom_decoder(design):
def __init__(self, num_outputs, fanout, strap_spacing, name="", route_layer="m1", output_layer="m1", invert_outputs=False):
# word lines in the base array become the address lines/cols in the decoder
# bit lines in the base array become the word lines/rows in the decoder
# array gets rotated 90deg so rows/cols switch
-
+ if "li" in layer:
+ self.output_layer = "m1"
+ self.inv_route_layer = "m1"
+ else:
+ self.output_layer = "m1"
+ self.inv_route_layer = "m3"
self.strap_spacing=strap_spacing
self.num_outputs = num_outputs
self.num_inputs = ceil(log(num_outputs, 2))
@@ -28,8 +32,6 @@ class rom_decoder(design):
b = factory.create(module_type=OPTS.bitcell)
self.cell_height = b.height
self.route_layer = route_layer
- self.output_layer = output_layer
- self.inv_route_layer = "m2"
self.fanout=fanout
self.invert_outputs=invert_outputs
self.create_netlist()
@@ -203,13 +205,12 @@ class rom_decoder(design):
for j in range(self.num_outputs):
self.copy_layout_pin(self.wordline_buf_inst, "out_{}".format(j), "wl_{}".format(j))
- offset = self.wordline_buf_inst.get_pin("out_{}".format(j)).center()
array_pins = [self.array_inst.get_pin("bl_0_{}".format(bl)) for bl in range(self.num_outputs)]
driver_pins = [self.wordline_buf_inst.get_pin("in_{}".format(bl)) for bl in range(self.num_outputs)]
route_pins = array_pins + driver_pins
- self.connect_row_pins(self.output_layer, route_pins, round=True)
+ self.connect_row_pins(self.inv_route_layer, route_pins, round=True)
def connect_inputs(self):
@@ -230,6 +231,23 @@ class rom_decoder(design):
self.add_path(self.inv_route_layer, [addr_out_pin.center(), addr_middle, addr_pin.center()])
self.add_path(self.inv_route_layer, [addr_bar_out_pin.center(), addr_bar_middle, addr_bar_pin.center()])
+
+ self.add_via_stack_center(offset=addr_pin.center(),
+ from_layer=addr_pin.layer,
+ to_layer=self.inv_route_layer)
+
+ self.add_via_stack_center(offset=addr_bar_pin.center(),
+ from_layer=addr_bar_pin.layer,
+ to_layer=self.inv_route_layer)
+
+ self.add_via_stack_center(offset=addr_out_pin.center(),
+ from_layer=addr_out_pin.layer,
+ to_layer=self.inv_route_layer)
+
+ self.add_via_stack_center(offset=addr_bar_out_pin.center(),
+ from_layer=addr_bar_out_pin.layer,
+ to_layer=self.inv_route_layer)
+
self.copy_layout_pin(self.buf_inst, "A{}_in".format(i), "A{}".format(i))
def route_supplies(self):
diff --git a/compiler/modules/rom_poly_tap.py b/compiler/modules/rom_poly_tap.py
index c16323b6..803211e4 100644
--- a/compiler/modules/rom_poly_tap.py
+++ b/compiler/modules/rom_poly_tap.py
@@ -13,9 +13,9 @@ from openram.tech import drc
class rom_poly_tap(design):
- def __init__(self, name="", cell_name=None, tx_type="nmos", strap_layer="m2", add_active_tap=False, place_poly=None):
+ def __init__(self, name="", cell_name=None, tx_type="nmos", strap_layer="m2", add_active_tap=False, place_poly=False):
super().__init__(name, cell_name)
- self.strap_layer=strap_layer
+ self.strap_layer = strap_layer
self.tx_type = tx_type
self.add_tap = add_active_tap
if place_poly is None:
@@ -35,16 +35,17 @@ class rom_poly_tap(design):
def create_layout(self):
self.place_via()
- self.add_boundary()
+
if self.add_tap or self.place_poly:
self.place_active_tap()
- self.extend_poly()
+
+ self.add_boundary()
+
def add_boundary(self):
contact_width = self.poly_contact.width
self.height = self.dummy.height
self.width = contact_width + self.pitch_offset
-
super().add_boundary()
def place_via(self):
@@ -69,8 +70,8 @@ class rom_poly_tap(design):
if self.tx_type == "pmos":
y_offset = -self.height
start = self.via.center() + vector(0, y_offset)
-
- self.add_segment_center("poly", start, vector(self.via.cx() + self.pitch_offset, self.via.cy() + y_offset))
+ if self.place_poly:
+ self.add_segment_center("poly", start, vector(self.via.cx() + self.pitch_offset, self.via.cy() + y_offset))
self.add_segment_center("poly", start, vector(0, self.via.cy() + y_offset))
def place_active_tap(self):
@@ -80,12 +81,9 @@ class rom_poly_tap(design):
tap_y = self.via.cy() + self.dummy.width * 0.5
contact_pos = vector(tap_x, tap_y)
- # edge of the next nmos
- active_edge = self.dummy.width - self.dummy.cell_inst.height - self.poly_extend_active
- # edge of the active contact
- tap_edge = tap_x + 0.5 * self.active_contact.height
- self.pitch_offset += (self.active_space * 2) - (tap_edge - active_edge) + self.contact_x_offset
+ # This pitch offset is used throughout the memory bank to make sure the pitch of the decoder outputs matches the pitch of the array inputs
+ self.pitch_offset = 0.5 * self.active_contact.width + self.active_space + 0.5 * self.contact_width + self.active_enclose_contact
if self.tx_type == "nmos" and self.add_tap:
self.add_via_center(layers=self.active_stack,
diff --git a/compiler/modules/rom_precharge_array.py b/compiler/modules/rom_precharge_array.py
index a450c968..716ab807 100644
--- a/compiler/modules/rom_precharge_array.py
+++ b/compiler/modules/rom_precharge_array.py
@@ -17,31 +17,17 @@ class rom_precharge_array(design):
"""
An array of inverters to create the inverted address lines for the rom decoder
"""
- def __init__(self, cols, name="", bitline_layer=None, strap_spacing=None, strap_layer="m2", tap_direction="row"):
+ def __init__(self, cols, name="", bitline_layer="m2", strap_spacing=0, strap_layer="m3", tap_direction="row"):
self.cols = cols
self.strap_layer = strap_layer
+ self.bitline_layer = bitline_layer
self.tap_direction = tap_direction
-
+ self.strap_spacing = strap_spacing
if "li" in layer:
self.supply_layer = "li"
else:
self.supply_layer = "m1"
- if bitline_layer is not None:
- self.bitline_layer = bitline_layer
- else:
- self.bitline_layer = self.supply_layer
-
-
- if name=="":
- name = "rom_inv_array_{0}".format(cols)
-
- if strap_spacing != None:
- self.strap_spacing = strap_spacing
- else:
- self.strap_spacing = 0
-
-
if strap_spacing != 0:
self.num_straps = ceil(self.cols / self.strap_spacing)
self.array_col_size = self.cols + self.num_straps
@@ -125,21 +111,19 @@ class rom_precharge_array(design):
# columns are bit lines
cell_x = 0
-
for col in range(self.cols):
if col % self.strap_spacing == 0:
- self.tap_insts[strap_num].place(vector(cell_x, cell_y + self.poly_tap.height))
+ self.tap_insts[strap_num].place(vector(cell_x + self.poly_space, cell_y + self.poly_tap.height))
strap_num += 1
if self.tap_direction == "col":
cell_x += self.poly_tap.pitch_offset
self.pmos_insts[col].place(vector(cell_x, cell_y))
- self.add_label("debug", "li", vector(cell_x, cell_y))
cell_x += self.pmos.width
- self.tap_insts[strap_num].place(vector(cell_x, cell_y + self.poly_tap.height))
+ self.tap_insts[strap_num].place(vector(cell_x + self.poly_space, cell_y + self.poly_tap.height))
def create_layout_pins(self):
self.copy_layout_pin(self.tap_insts[0], "poly_tap", "gate")
@@ -151,7 +135,15 @@ class rom_precharge_array(design):
def route_supply(self):
- self.route_horizontal_pins("vdd", insts=self.pmos_insts, layer=self.strap_layer)
+ # Hacky way to route all the vdd pins together and then create a layout pin on only one side.
+ self.route_horizontal_pins("vdd", insts=self.pmos_insts, layer=self.strap_layer, new_name="vdd_tmp")
+
+ tmp_vdd = self.get_pin("vdd_tmp")
+
+ self.add_layout_pin_rect_center("vdd", layer=self.strap_layer, offset=tmp_vdd.lc(), height=tmp_vdd.height())
+ self.add_segment_center(layer=self.strap_layer, start=tmp_vdd.lc(), end=tmp_vdd.rc(), width=tmp_vdd.height())
+ self.remove_layout_pin("vdd_tmp")
+
def connect_taps(self):
array_pins = [self.tap_insts[i].get_pin("poly_tap") for i in range(len(self.tap_insts))]
@@ -161,17 +153,24 @@ class rom_precharge_array(design):
for tap in self.tap_insts:
tap_pin = tap.get_pin("poly_tap")
start = vector(tap_pin.cx(), tap_pin.by())
- end = vector(start.x, tap.mod.get_pin("poly_tap").cy())
+ end = vector(start.x, self.pmos_insts[0].get_pin("G").cy())
self.add_segment_center(layer="poly", start=start, end=end)
offset_start = vector(end.x - self.poly_tap.width + self.poly_extend_active, end.y)
offset_end = end + vector(0.5*self.poly_width, 0)
self.add_segment_center(layer="poly", start=offset_start, end=offset_end)
+ self.add_segment_center(layer="poly", start=self.pmos_insts[-1].get_pin("G").center(), end=offset_end)
+
+
+ gate_y = self.pmos_insts[0].get_pin('G').cy()
+ start = vector( self.get_pin("gate").lx(), gate_y)
+ end = vector( self.get_pin("precharge_r").rx(), gate_y )
+
+ self.add_segment_center(layer="poly", start=start, end=end)
def extend_well(self):
self.well_offset = self.pmos.tap_offset
- well_y = self.pmos_insts[0].get_pin("vdd").cy() - 0.5 * self.nwell_width
- well_y = self.get_pin("vdd").cy() - 0.5 * self.nwell_width
+ well_y = self.get_pin("vdd").by() - self.nwell_enclose_active
well_ll = vector(0, well_y)
self.add_rect("nwell", well_ll, self.width , self.height - well_y)
\ No newline at end of file
diff --git a/compiler/modules/rom_precharge_cell.py b/compiler/modules/rom_precharge_cell.py
index 3d4730aa..f9517b6b 100644
--- a/compiler/modules/rom_precharge_cell.py
+++ b/compiler/modules/rom_precharge_cell.py
@@ -25,8 +25,12 @@ class rom_precharge_cell(rom_base_cell):
self.place_tap()
self.extend_well()
+
def add_modules(self):
- width = pgate.nearest_bin("pmos", drc["minwidth_tx"])
+ if OPTS.tech_name == "sky130":
+ width = pgate.nearest_bin("pmos", drc["minwidth_tx"])
+ else:
+ width = drc("minwidth_tx")
self.pmos = factory.create(module_type="ptx",
module_name="pre_pmos_mod",
tx_type="pmos",
@@ -52,20 +56,18 @@ class rom_precharge_cell(rom_base_cell):
self.poly_size = (self.cell_inst.width + self.active_space) - (self.cell_inst.height + 2 * self.poly_extend_active)
def extend_well(self):
-
- well_y = self.get_pin("vdd").cy() - 0.5 * self.nwell_width
+ well_y = self.get_pin("vdd").cy() - 0.5 * self.tap.height - self.nwell_enclose_active
well_ll = vector(0, well_y)
- height = self.get_pin("D").cy() + 0.5 * self.nwell_width - well_y
+ height = self.get_pin("D").cy() + self.nwell_enclose_active - well_y
self.add_rect("nwell", well_ll, self.width , height)
def place_tap(self):
source = self.cell_inst.get_pin("S")
-
- tap_y = source.cy() - self.contact_width - 2 * self.active_enclose_contact - self.active_space
+ tap_y = source.cy() - self.contact_width - 5 * self.active_enclose_contact - self.active_space
self.tap_offset = abs(tap_y)
pos = vector(source.cx(), tap_y )
- self.add_via_center(layers=self.active_stack,
+ self.tap = self.add_via_center(layers=self.active_stack,
offset=pos,
implant_type="n",
well_type="n",
@@ -83,4 +85,7 @@ class rom_precharge_cell(rom_base_cell):
self.remove_layout_pin("S")
def place_bitline(self):
+ pass
+
+ def short_gate(self):
pass
\ No newline at end of file
diff --git a/compiler/modules/rom_wordline_driver_array.py b/compiler/modules/rom_wordline_driver_array.py
index 44deac1c..d6b42249 100644
--- a/compiler/modules/rom_wordline_driver_array.py
+++ b/compiler/modules/rom_wordline_driver_array.py
@@ -88,8 +88,6 @@ class rom_wordline_driver_array(design):
Add a pin for each row of vdd/gnd which
are must-connects next level up.
"""
- # self.route_vertical_pins("vdd", self.wld_inst, xside="cx", layer=self.supply_layer)
- # self.route_vertical_pins("gnd", self.wld_inst, xside="cx", layer=self.supply_layer)
if not self.invert_outputs:
vdd_pins = [pin for inst in self.wld_inst for pin in inst.get_pins("vdd")]
gnd_pins = [pin for inst in self.wld_inst for pin in inst.get_pins("gnd")]
@@ -110,13 +108,13 @@ class rom_wordline_driver_array(design):
# Place the top level supply pins on the edge of the module
for pin in self.get_pins("gnd_tmp"):
- bottom = vector(pin.cx(), pin.by() - 0.5 * supply_width)
- top = vector(pin.cx(), pin.uy() + 0.5 * supply_width)
+ bottom = vector(pin.cx(), pin.by())
+ top = vector(pin.cx(), pin.uy())
self.add_layout_pin_rect_ends(layer=self.supply_layer, start=bottom, end=top, name="gnd")
for pin in self.get_pins("vdd_tmp"):
- bottom = vector(pin.cx(), pin.by() - 0.5 * supply_width)
- top = vector(pin.cx(), pin.uy() + 0.5 * supply_width)
+ bottom = vector(pin.cx(), pin.by())
+ top = vector(pin.cx(), pin.uy())
self.add_layout_pin_rect_ends(layer=self.supply_layer, start=bottom, end=top, name="vdd")
@@ -214,7 +212,8 @@ class rom_wordline_driver_array(design):
directions="nonpref")
self.add_via_stack_center(offset=offset,
from_layer=self.active_stack[2],
- to_layer=self.supply_layer)
+ to_layer=self.supply_layer,
+ directions="nonpref")
if well_type == "p":
pin = "gnd_tap"
self.gnd_taps.append(self.add_layout_pin_rect_center(text=pin, layer=self.supply_layer, offset=offset))
diff --git a/compiler/tests/04_rom_address_control_buf_test.py b/compiler/tests/04_rom_address_control_buf_test.py
new file mode 100644
index 00000000..38bf0137
--- /dev/null
+++ b/compiler/tests/04_rom_address_control_buf_test.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2023 Regents of the University of California and The Board
+# of Regents for the Oklahoma Agricultural and Mechanical College
+# (acting for and on behalf of Oklahoma State University)
+# All rights reserved.
+#
+import sys, os
+import unittest
+from testutils import *
+
+import openram
+from openram import debug
+from openram.sram_factory import factory
+from openram import OPTS
+
+
+class precharge_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ openram.init_openram(config_file, is_unit_test=True)
+
+ # check precharge in single port
+ debug.info(2, "Testing rom address control buffer")
+
+
+ tx = factory.create(module_type="rom_address_control_buf", module_name="address_control_cell", size=6)
+ self.local_check(tx)
+
+ openram.end_openram()
+
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = openram.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/04_rom_precharge_test.py b/compiler/tests/04_rom_precharge_test.py
new file mode 100644
index 00000000..862e6cb0
--- /dev/null
+++ b/compiler/tests/04_rom_precharge_test.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2023 Regents of the University of California and The Board
+# of Regents for the Oklahoma Agricultural and Mechanical College
+# (acting for and on behalf of Oklahoma State University)
+# All rights reserved.
+#
+import sys, os
+import unittest
+from testutils import *
+
+import openram
+from openram import debug
+from openram.sram_factory import factory
+from openram import OPTS
+
+
+class precharge_test(openram_test):
+
+ def runTest(self):
+ config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
+ openram.init_openram(config_file, is_unit_test=True)
+
+ # check precharge in single port
+ debug.info(2, "Testing rom precharge bitcell")
+
+
+ tx = factory.create(module_type="rom_precharge_array", module_name="rom_precharge_array", cols=8, strap_spacing=2, tap_direction="col")
+ self.local_check(tx)
+
+ openram.end_openram()
+
+
+# run the test from the command line
+if __name__ == "__main__":
+ (OPTS, args) = openram.parse_args()
+ del sys.argv[1:]
+ header(__file__, OPTS.tech_name)
+ unittest.main(testRunner=debugTestRunner())
diff --git a/compiler/tests/14_rom_array_test.py b/compiler/tests/05_rom_array_test.py
similarity index 71%
rename from compiler/tests/14_rom_array_test.py
rename to compiler/tests/05_rom_array_test.py
index c7a03ea1..80e188af 100755
--- a/compiler/tests/14_rom_array_test.py
+++ b/compiler/tests/05_rom_array_test.py
@@ -25,9 +25,11 @@ class rom_array_test(openram_test):
debug.info(2, "Testing 4x4 array for rom cell")
- data = [[1, 0, 0, 1, 0, 0, 1, 1, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 1], [0, 1, 0, 0, 1, 1, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 1, 0, 0], [1, 0, 0, 1, 0, 0, 0, 1, 0]]
+ # data = [[1, 0, 0, 1, 0, 0, 1, 1, 0], [0, 1, 0, 0, 1, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 1], [0, 1, 0, 0, 1, 1, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 1, 0, 0], [1, 0, 0, 1, 0, 0, 0, 1, 0]]
- a = factory.create(module_type="rom_base_array", cols=9, rows=8, bitmap=data, strap_spacing=4, pitch_match=True)
+
+ data = [[1, 0, 0], [1, 1, 1], [0,1, 0]]
+ a = factory.create(module_type="rom_base_array", cols=3, rows=3, bitmap=data, strap_spacing=1, pitch_match=True)
self.local_check(a)
a.sp_write(OPTS.openram_temp + 'simulation_file.sp')
diff --git a/compiler/tests/Makefile b/compiler/tests/Makefile
index cdef60ca..0b1b9de0 100644
--- a/compiler/tests/Makefile
+++ b/compiler/tests/Makefile
@@ -4,6 +4,7 @@ include $(TOP_DIR)/openram.mk
.DEFAULT_GOAL := all
ARGS ?=
+TEST_TECHS ?= scn4m_subm freepdk45
TECHS ?= scn4m_subm freepdk45 sky130
TEST_DIR = $(TOP_DIR)/compiler/tests
@@ -63,14 +64,16 @@ BROKEN_STAMPS = \
%/50_riscv_512b_1rw_func_test.ok \
%/50_riscv_8k_1rw1r_func_test.ok \
%/50_riscv_8k_1rw_func_test.ok \
+ freepdk45/04_rom_address_control_buf_test.ok \
+ freepdk45/05_rom_array_test.ok \
freepdk45/06_rom_decoder_test.ok \
freepdk45/07_rom_column_mux_array_test.ok \
freepdk45/08_rom_decoder_buffer_array_test.ok \
freepdk45/08_rom_precharge_array_test.ok \
freepdk45/10_rom_wordline_driver_array_test.ok \
- freepdk45/14_rom_array_test.ok \
freepdk45/16_rom_control_logic_test.ok \
freepdk45/19_rom_bank_test.ok \
+ scn4m_subm/04_rom_address_control_buf_test.ok \
scn4m_subm/06_rom_decoder_test.ok \
scn4m_subm/07_rom_column_mux_array_test.ok \
scn4m_subm/08_rom_decoder_buffer_array_test.ok \
diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py
index 2c6a466e..ae58c80c 100644
--- a/compiler/verify/magic.py
+++ b/compiler/verify/magic.py
@@ -188,6 +188,9 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write("expand\n")
f.write('puts "Finished expanding"\n')
f.write("drc euclidean on\n")
+ # Workaround to address DRC CIF style not loading if 'drc check' is run before catchup
+ if OPTS.tech_name=="gf180mcu":
+ f.write("drc catchup\n")
f.write("drc check\n")
f.write('puts "Finished drc check"\n')
f.write("drc catchup\n")
diff --git a/docker/Dockerfile b/docker/Dockerfile
index fc165e19..62e0a6d5 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -130,14 +130,14 @@ RUN apt-get install --no-install-recommends -y iverilog
### Magic ###
#ARG MAGIC_COMMIT=db4fa65bfc096e63954b37b188ea27b90ab31839
#ARG MAGIC_COMMIT=8.3.274
-ARG MAGIC_COMMIT=8.3.311
+ARG MAGIC_COMMIT=8.3.363
WORKDIR /root
#RUN git clone https://github.com/RTimothyEdwards/magic.git magic
RUN git clone git://opencircuitdesign.com/magic magic
WORKDIR /root/magic
RUN git checkout ${MAGIC_COMMIT}
-COPY mrg.patch /root/magic
-RUN git apply mrg.patch
+#COPY mrg.patch /root/magic
+#RUN git apply mrg.patch
RUN ./configure
RUN make
RUN make install
diff --git a/docs/source/basic_setup.md b/docs/source/basic_setup.md
index af73ae55..f238dbc3 100644
--- a/docs/source/basic_setup.md
+++ b/docs/source/basic_setup.md
@@ -139,7 +139,9 @@ make install
You can also run these from the package installation directory if you have the
OpenRAM library.
+## GF180 Setup
+OpenRAM currently **does not** support gf180mcu for SRAM generation. However ROM generation for gf180mcu is supported as an experimental feature. To set up gf180mcu, first change ```OPEN_PDKS_GIT_COMMIT``` in ```OpenRAM/Makefile``` to version 1.0.395. Then follow the setup instructions for Sky130 but run ```make gf180-pdk``` instead of ```make pdk```.
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
diff --git a/docs/source/index.md b/docs/source/index.md
index a1f7079a..8efb19c6 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -110,7 +110,7 @@ Commercial tools (optional):
* Michael Grimes
* Jennifer Sowash
* Jesse Cirimelli-Low
-
+
https://www.youtube.com/watch?v=rd5j8mG24H4&t=0s
* Many other past students:
* Jeff Butera
* Tom Golubev
diff --git a/setpaths.sh b/setpaths.sh
index efceed78..79c2ff1a 100755
--- a/setpaths.sh
+++ b/setpaths.sh
@@ -6,4 +6,5 @@
export OPENRAM_HOME="`pwd`/compiler"
export OPENRAM_TECH="`pwd`/technology"
+export PDK_ROOT="$HOME/gf/pdk"
export PYTHONPATH=$OPENRAM_HOME
diff --git a/technology/gf180mcu/__init__.py b/technology/gf180mcu/__init__.py
new file mode 100644
index 00000000..48aedd88
--- /dev/null
+++ b/technology/gf180mcu/__init__.py
@@ -0,0 +1,52 @@
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 Regents of the University of California and The Board
+# of Regents for the Oklahoma Agricultural and Mechanical College
+# (acting for and on behalf of Oklahoma State University)
+# All rights reserved.
+#
+#!/usr/bin/python
+"""
+This type of setup script should be placed in the setup_scripts directory in the trunk
+"""
+
+import sys
+import os
+
+TECHNOLOGY = "gf180mcu"
+
+os.environ["MGC_TMPDIR"] = "/tmp"
+
+###########################
+# OpenRAM Paths
+
+# OpenPDK needed for magicrc, tech file and spice models of transistors
+if 'PDK_ROOT' in os.environ:
+ open_pdks = os.path.join(os.environ['PDK_ROOT'], 'gf180mcuD', 'libs.tech')
+else:
+ raise SystemError("Unable to find open_pdks tech file. Set PDK_ROOT.")
+
+# The ngspice models work with Xyce too now
+spice_model_dir = os.path.join(open_pdks, "ngspice")
+gf180_lib_ngspice = os.path.join(open_pdks, "ngspice", "sm141064.ngspice")
+if not os.path.exists(gf180_lib_ngspice):
+ raise SystemError("Did not find {} under {}".format(gf180_lib_ngspice, open_pdks))
+os.environ["SPICE_MODEL_DIR"] = spice_model_dir
+
+open_pdks = os.path.abspath(open_pdks)
+gf180_magicrc = os.path.join(open_pdks, 'magic', "gf180mcuD.magicrc")
+if not os.path.exists(gf180_magicrc):
+ raise SystemError("Did not find {} under {}".format(gf180_magicrc, open_pdks))
+os.environ["OPENRAM_MAGICRC"] = gf180_magicrc
+gf180_netgenrc = os.path.join(open_pdks, 'netgen', "setup.tcl")
+if not os.path.exists(gf180_netgenrc):
+ raise SystemError("Did not find {} under {}".format(gf180_netgenrc, open_pdks))
+os.environ["OPENRAM_NETGENRC"] = gf180_netgenrc
+
+try:
+ DRCLVS_HOME = os.path.abspath(os.environ.get("DRCLVS_HOME"))
+except:
+ DRCLVS_HOME= "not-found"
+os.environ["DRCLVS_HOME"] = DRCLVS_HOME
+
+
diff --git a/technology/gf180mcu/custom/gf180_bitcell.py b/technology/gf180mcu/custom/gf180_bitcell.py
new file mode 100644
index 00000000..91be2e1b
--- /dev/null
+++ b/technology/gf180mcu/custom/gf180_bitcell.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2022 Regents of the University of California
+# All rights reserved.
+#
+
+from openram import debug
+from openram.modules import bitcell_base
+from openram.tech import cell_properties as props
+
+
+class gf180_bitcell(bitcell_base):
+ """
+ A single bit cell (6T, 8T, etc.) This module implements the
+ single memory cell used in the design. It is a hand-made cell, so
+ the layout and netlist should be available in the technology
+ library.
+ """
+
+ def __init__(self, version="opt1", name=""):
+ cell_name = "cell1rw"
+ super().__init__(name, cell_name=cell_name, prop=props.bitcell_1port)
+ debug.info(2, "Create bitcell")
+
+ def build_graph(self, graph, inst_name, port_nets):
+ """
+ Adds edges based on inputs/outputs.
+ Overrides base class function.
+ """
+ self.add_graph_edges(graph, port_nets)
diff --git a/technology/gf180mcu/gds_lib/cell1rw.gds b/technology/gf180mcu/gds_lib/cell1rw.gds
new file mode 100644
index 00000000..70b18d37
Binary files /dev/null and b/technology/gf180mcu/gds_lib/cell1rw.gds differ
diff --git a/technology/gf180mcu/gds_lib/gf180mcu_3v3__nand2_1_dec.gds b/technology/gf180mcu/gds_lib/gf180mcu_3v3__nand2_1_dec.gds
new file mode 100644
index 00000000..dfafe19c
Binary files /dev/null and b/technology/gf180mcu/gds_lib/gf180mcu_3v3__nand2_1_dec.gds differ
diff --git a/technology/gf180mcu/mag_lib/.magicrc b/technology/gf180mcu/mag_lib/.magicrc
new file mode 100644
index 00000000..1fa2e18d
--- /dev/null
+++ b/technology/gf180mcu/mag_lib/.magicrc
@@ -0,0 +1,69 @@
+###
+### Source file TECHNAME.magicrc
+### Process this file with the m4 processor
+###
+puts stdout "Sourcing design .magicrc for technology TECHNAME ..."
+
+# Put internal grid on 0.005 pitch. This is important to match vendor file
+# input (as opposed to SCMOS-style layout. The default lambda grid is 0.05um).
+
+set scalefac [tech lambda]
+if {[lindex $scalefac 1] < 10} {
+ scalegrid 1 10
+}
+
+# drc off
+drc euclidean on
+# Change this to a fixed number for repeatable behavior with GDS writes
+# e.g., "random seed 12345"
+catch {random seed}
+
+# Allow override of PDK path from environment variable PDK_ROOT
+# "file nativename" guards against a local PDK_ROOT with "~" in the name
+if {[catch {set PDK_ROOT [file nativename $env(PDK_ROOT)]}]} {
+ set PDK_ROOT STAGING_PATH
+}
+
+# loading technology
+tech load $PDK_ROOT/TECHNAME/MAGIC_CURRENT/TECHNAME.tech
+
+# load device generator
+source $PDK_ROOT/TECHNAME/MAGIC_CURRENT/TECHNAME.tcl
+
+# load bind keys
+# source $PDK_ROOT/TECHNAME/MAGIC_CURRENT/TECHNAME-BindKeys
+
+# set units to lambda grid
+snap lambda
+
+# set gf180mcu standard power, ground, and substrate names
+set VDD VDD
+set GND VSS
+set SUB VSUBS
+
+# Allow override of type of magic library views used, "mag" or "maglef",
+# from environment variable MAGTYPE
+
+if {[catch {set MAGTYPE $env(MAGTYPE)}]} {
+ set MAGTYPE mag
+}
+
+# add path to reference cells
+if {[file isdir ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}]} {
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_fd_pr
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_mcu7t5v0
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_mcu9t5v0
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_fd_io
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_fd_ip_sram
+} else {
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_pr/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_sc_mcu7t5v0/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_sc_mcu9t5v0/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_io/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_ip_sram/${MAGTYPE}
+}
+
+# add path to IP from catalog. This procedure defined in the PDK script.
+catch {magic::query_mylib_ip}
+# add path to local IP from user design space. Defined in the PDK script.
+catch {magic::query_my_projects}
diff --git a/technology/gf180mcu/mag_lib/gf180mcu_3v3__nand2_1_dec.mag b/technology/gf180mcu/mag_lib/gf180mcu_3v3__nand2_1_dec.mag
new file mode 100644
index 00000000..4182347f
--- /dev/null
+++ b/technology/gf180mcu/mag_lib/gf180mcu_3v3__nand2_1_dec.mag
@@ -0,0 +1,165 @@
+magic
+tech gf180mcuD
+magscale 1 10
+timestamp 1694553776
+<< nwell >>
+rect 675 -30 1355 650
+<< nmos >>
+rect 211 310 381 370
+rect 211 200 381 260
+<< pmos >>
+rect 765 340 1106 400
+rect 765 170 1106 230
+<< ndiff >>
+rect 211 448 381 470
+rect 211 402 273 448
+rect 319 402 381 448
+rect 211 370 381 402
+rect 211 260 381 310
+rect 211 168 381 200
+rect 211 122 273 168
+rect 319 122 381 168
+rect 211 100 381 122
+<< pdiff >>
+rect 765 478 1106 500
+rect 765 432 818 478
+rect 1052 432 1106 478
+rect 765 400 1106 432
+rect 765 308 1106 340
+rect 765 262 818 308
+rect 1052 262 1106 308
+rect 765 230 1106 262
+rect 765 138 1106 170
+rect 765 92 818 138
+rect 1052 92 1106 138
+rect 765 70 1106 92
+<< ndiffc >>
+rect 273 402 319 448
+rect 273 122 319 168
+<< pdiffc >>
+rect 818 432 1052 478
+rect 818 262 1052 308
+rect 818 92 1052 138
+<< psubdiff >>
+rect 74 33 181 50
+rect 74 -13 114 33
+rect 160 -13 181 33
+rect 74 -30 181 -13
+<< nsubdiff >>
+rect 1172 117 1252 154
+rect 1172 71 1189 117
+rect 1235 71 1252 117
+rect 1172 47 1252 71
+<< psubdiffcont >>
+rect 114 -13 160 33
+<< nsubdiffcont >>
+rect 1189 71 1235 117
+<< polysilicon >>
+rect 88 383 171 410
+rect 88 337 104 383
+rect 150 370 171 383
+rect 431 370 765 400
+rect 150 337 211 370
+rect 88 310 211 337
+rect 381 340 765 370
+rect 1106 340 1156 400
+rect 381 310 471 340
+rect 88 244 211 260
+rect 88 198 104 244
+rect 150 200 211 244
+rect 381 230 471 260
+rect 381 200 765 230
+rect 150 198 171 200
+rect 88 160 171 198
+rect 431 170 765 200
+rect 1106 170 1156 230
+<< polycontact >>
+rect 104 337 150 383
+rect 104 198 150 244
+<< metal1 >>
+rect 260 448 630 450
+rect 101 383 153 435
+rect 260 402 273 448
+rect 319 402 630 448
+rect 807 432 818 478
+rect 1052 432 1064 478
+rect 903 426 915 432
+rect 967 426 979 432
+rect 260 400 630 402
+rect 101 337 104 383
+rect 150 337 153 383
+rect 101 323 153 337
+rect 580 310 630 400
+rect 580 308 1182 310
+rect 580 262 818 308
+rect 1052 262 1182 308
+rect 580 260 1182 262
+rect 101 244 153 258
+rect 101 198 104 244
+rect 150 198 153 244
+rect 101 139 153 198
+rect 241 116 273 168
+rect 325 116 348 168
+rect 903 138 915 144
+rect 967 138 979 144
+rect 241 110 348 116
+rect 807 92 818 138
+rect 1052 92 1064 138
+rect 1139 68 1186 120
+rect 1238 68 1250 120
+rect 80 36 179 46
+rect 80 -16 111 36
+rect 163 -16 179 36
+rect 80 -24 179 -16
+<< via1 >>
+rect 915 432 967 478
+rect 915 426 967 432
+rect 273 122 319 168
+rect 319 122 325 168
+rect 273 116 325 122
+rect 915 138 967 144
+rect 915 92 967 138
+rect 1186 117 1238 120
+rect 1186 71 1189 117
+rect 1189 71 1235 117
+rect 1235 71 1238 117
+rect 1186 68 1238 71
+rect 111 33 163 36
+rect 111 -13 114 33
+rect 114 -13 160 33
+rect 160 -13 163 33
+rect 111 -16 163 -13
+<< metal2 >>
+rect 271 168 327 530
+rect 271 116 273 168
+rect 325 116 327 168
+rect 271 38 327 116
+rect 89 36 327 38
+rect 89 -16 111 36
+rect 163 -16 327 36
+rect 913 478 969 530
+rect 913 426 915 478
+rect 967 426 969 478
+rect 913 144 969 426
+rect 913 92 915 144
+rect 967 122 969 144
+rect 967 120 1250 122
+rect 967 92 1186 120
+rect 913 68 1186 92
+rect 1238 68 1250 120
+rect 913 66 1250 68
+rect 913 18 969 66
+rect 89 -18 327 -16
+<< labels >>
+rlabel metal2 s 271 38 327 530 4 GND
+port 1 nsew
+rlabel metal2 s 941 43 941 43 4 VDD
+flabel metal1 s 605 425 605 425 2 FreeSans 368 0 0 0 Z
+port 2 nsew
+flabel metal1 s 127 360 127 360 2 FreeSans 368 0 0 0 A
+port 3 nsew
+flabel metal1 s 127 221 127 221 2 FreeSans 368 0 0 0 B
+port 4 nsew
+<< properties >>
+string FIXED_BBOX -17 0 1373 542
+<< end >>
diff --git a/technology/gf180mcu/sp_lib/cell1rw.sp b/technology/gf180mcu/sp_lib/cell1rw.sp
new file mode 100644
index 00000000..7070979a
--- /dev/null
+++ b/technology/gf180mcu/sp_lib/cell1rw.sp
@@ -0,0 +1,11 @@
+* NGSPICE file created from cell1rw.ext - technology: gf180mcuC
+
+.subckt cell1rw BL BR GND VDD nwell pwell WL
+X0 GND a_63_149# a_18_103# pwell nfet_06v0 ad=0.627p pd=3.74u as=0.7275p ps=4.76u w=0.95u l=0.6u
+X1 a_18_103# WL BL pwell nfet_06v0 ad=0p pd=0u as=0.282p ps=2.14u w=0.6u l=0.77u
+X2 a_63_149# a_18_103# VDD nwell pfet_06v0 ad=0.27p pd=2.1u as=0.489p ps=3.38u w=0.6u l=0.6u
+X3 a_63_149# WL BR pwell nfet_06v0 ad=0.7275p pd=4.76u as=0.282p ps=2.14u w=0.6u l=0.77u
+X4 VDD a_63_149# a_18_103# nwell pfet_06v0 ad=0p pd=0u as=0.27p ps=2.1u w=0.6u l=0.6u
+X5 a_63_149# a_18_103# GND pwell nfet_06v0 ad=0p pd=0u as=0p ps=0u w=0.95u l=0.6u
+.ends
+
diff --git a/technology/gf180mcu/sp_lib/gf180mcu_3v3__nand2_1_dec.sp b/technology/gf180mcu/sp_lib/gf180mcu_3v3__nand2_1_dec.sp
new file mode 100644
index 00000000..69bfd9d8
--- /dev/null
+++ b/technology/gf180mcu/sp_lib/gf180mcu_3v3__nand2_1_dec.sp
@@ -0,0 +1,6 @@
+.subckt gf180mcu_3v3__nand2_1_dec A B Z VDD GND
+X0 VDD B Z VDD pfet_03v3 w=1.7u l=0.3u
+X1 Z A VDD VDD pfet_03v3 w=1.7u l=0.3u
+X2 a_28_21# A Z GND nfet_03v3 w=0.85u l=0.3u
+X3 GND B a_28_21# GND nfet_03v3 w=0.85u l=0.3u
+.ends
diff --git a/technology/gf180mcu/tech/.magicrc b/technology/gf180mcu/tech/.magicrc
new file mode 100644
index 00000000..1fa2e18d
--- /dev/null
+++ b/technology/gf180mcu/tech/.magicrc
@@ -0,0 +1,69 @@
+###
+### Source file TECHNAME.magicrc
+### Process this file with the m4 processor
+###
+puts stdout "Sourcing design .magicrc for technology TECHNAME ..."
+
+# Put internal grid on 0.005 pitch. This is important to match vendor file
+# input (as opposed to SCMOS-style layout. The default lambda grid is 0.05um).
+
+set scalefac [tech lambda]
+if {[lindex $scalefac 1] < 10} {
+ scalegrid 1 10
+}
+
+# drc off
+drc euclidean on
+# Change this to a fixed number for repeatable behavior with GDS writes
+# e.g., "random seed 12345"
+catch {random seed}
+
+# Allow override of PDK path from environment variable PDK_ROOT
+# "file nativename" guards against a local PDK_ROOT with "~" in the name
+if {[catch {set PDK_ROOT [file nativename $env(PDK_ROOT)]}]} {
+ set PDK_ROOT STAGING_PATH
+}
+
+# loading technology
+tech load $PDK_ROOT/TECHNAME/MAGIC_CURRENT/TECHNAME.tech
+
+# load device generator
+source $PDK_ROOT/TECHNAME/MAGIC_CURRENT/TECHNAME.tcl
+
+# load bind keys
+# source $PDK_ROOT/TECHNAME/MAGIC_CURRENT/TECHNAME-BindKeys
+
+# set units to lambda grid
+snap lambda
+
+# set gf180mcu standard power, ground, and substrate names
+set VDD VDD
+set GND VSS
+set SUB VSUBS
+
+# Allow override of type of magic library views used, "mag" or "maglef",
+# from environment variable MAGTYPE
+
+if {[catch {set MAGTYPE $env(MAGTYPE)}]} {
+ set MAGTYPE mag
+}
+
+# add path to reference cells
+if {[file isdir ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}]} {
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_fd_pr
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_mcu7t5v0
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_mcu9t5v0
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_fd_io
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/${MAGTYPE}/gf180mcu_fd_ip_sram
+} else {
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_pr/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_sc_mcu7t5v0/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_sc_mcu9t5v0/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_io/${MAGTYPE}
+ addpath ${PDK_ROOT}/TECHNAME/libs.ref/gf180mcu_fd_ip_sram/${MAGTYPE}
+}
+
+# add path to IP from catalog. This procedure defined in the PDK script.
+catch {magic::query_mylib_ip}
+# add path to local IP from user design space. Defined in the PDK script.
+catch {magic::query_my_projects}
diff --git a/technology/gf180mcu/tech/__init__.py b/technology/gf180mcu/tech/__init__.py
new file mode 100644
index 00000000..c6bd5798
--- /dev/null
+++ b/technology/gf180mcu/tech/__init__.py
@@ -0,0 +1,12 @@
+#!/usr/bin/env python3
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2022 Regents of the University of California
+# All rights reserved.
+#
+
+"""
+Import tech specific modules.
+"""
+
+from .tech import *
diff --git a/technology/gf180mcu/tech/gf180mcu.lym b/technology/gf180mcu/tech/gf180mcu.lym
new file mode 100644
index 00000000..c819e2ea
--- /dev/null
+++ b/technology/gf180mcu/tech/gf180mcu.lym
@@ -0,0 +1,49 @@
+
+
+
+
+
+ pymacros
+
+
+
+ true
+ false
+
+ false
+
+
+ python
+
+
+
+import sys
+import os
+
+technology_macros_path = os.path.dirname(os.path.abspath(__file__))
+sys.path.insert(0, technology_macros_path)
+
+from cells import gf180mcu
+
+# Instantiate and register the library
+gf180mcu()
+
+print("## gf180mcu PDK Pcells loaded.")
+print(sys.path)
+
+
+
diff --git a/technology/gf180mcu/tech/gf180mcu.lyp b/technology/gf180mcu/tech/gf180mcu.lyp
new file mode 100644
index 00000000..1b996571
--- /dev/null
+++ b/technology/gf180mcu/tech/gf180mcu.lyp
@@ -0,0 +1,2008 @@
+
+
+
+
+ #55ce57
+ #55ce57
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ 1/222@1
+
+
+ #661a48
+ #661a48
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ pass_mk 2/222@1
+
+
+ #7c6078
+ #7c6078
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ fail_mk 3/222@1
+
+
+ #f26f6c
+ #f26f6c
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ polygon_mk 4/222@1
+
+
+ #324416
+ #324416
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ 5/222@1
+
+
+ #3acb88
+ #3acb88
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ violation_mk 6/222@1
+
+
+ #5a68c2
+ #5a68c2
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ rule_txt_mk 11/222@1
+
+
+ #718e2d
+ #718e2d
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ 12/222@1
+
+
+ #3f3f3c
+ #3f3f3c
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ case_txt_mk 13/222@1
+
+
+ #3f12e4
+ #3f12e4
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ 14/222@1
+
+
+ #f9bd46
+ #f9bd46
+ 0
+ 0
+ I3
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ 15/222@1
+
+
+ #bd74bd
+ #bd74bd
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ COMP 22/0@1
+
+
+ #88cb1d
+ #88cb1d
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ DNWELL 12/0@1
+
+
+ #f9946b
+ #f9946b
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Nwell 21/0@1
+
+
+ #3a2888
+ #3a2888
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ LVPWELL 204/0@1
+
+
+ #c16f5c
+ #c16f5c
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Dualgate 55/0@1
+
+
+ #2e9521
+ #2e9521
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Poly2 30/0@1
+
+
+ #bf3efb
+ #bf3efb
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Nplus 32/0@1
+
+
+ #34c590
+ #34c590
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Pplus 31/0@1
+
+
+ #67392f
+ #67392f
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ SAB 49/0@1
+
+
+ #d9ef03
+ #d9ef03
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ ESD 24/0@1
+
+
+ #c86634
+ #c86634
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Contact 33/0@1
+
+
+ #eddd07
+ #eddd07
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal1 34/0@1
+
+
+ #f5e7f1
+ #f5e7f1
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Via1 35/0@1
+
+
+ #ccf338
+ #ccf338
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal2 36/0@1
+
+
+ #e1d8ca
+ #e1d8ca
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Via2 38/0@1
+
+
+ #97b91b
+ #97b91b
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal3 42/0@1
+
+
+ #53e2e8
+ #53e2e8
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Via3 40/0@1
+
+
+ #80317c
+ #80317c
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal4 46/0@1
+
+
+ #a7b1d1
+ #a7b1d1
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Via4 41/0@1
+
+
+ #cdc16b
+ #cdc16b
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal5 81/0@1
+
+
+ #827e5b
+ #827e5b
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Via5 82/0@1
+
+
+ #b1dd9c
+ #b1dd9c
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MetalTop 53/0@1
+
+
+ #72342b
+ #72342b
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Pad 37/0@1
+
+
+ #1437ff
+ #1437ff
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Resistor 62/0@1
+
+
+ #82b18c
+ #82b18c
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ FHRES 227/0@1
+
+
+ #b9a4fd
+ #b9a4fd
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ FuseTop 75/0@1
+
+
+ #87ad41
+ #87ad41
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ FuseWindow_D 96/1@1
+
+
+ #ec7ceb
+ #ec7ceb
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ POLYFUSE 220/0@1
+
+
+ #e29901
+ #e29901
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MVSD 210/0@1
+
+
+ #63a2db
+ #63a2db
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MVPSD 11/39@1
+
+
+ #91d339
+ #91d339
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ NAT 5/0@1
+
+
+ #f84e38
+ #f84e38
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ COMP_Dummy 22/4@1
+
+
+ #2d4da8
+ #2d4da8
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Poly2_Dummy 30/4@1
+
+
+ #c01202
+ #c01202
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal1_Dummy 34/4@1
+
+
+ #2f93c0
+ #2f93c0
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal2_Dummy 36/4@1
+
+
+ #867300
+ #867300
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal3_Dummy 42/4@1
+
+
+ #e86c70
+ #e86c70
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal4_Dummy 46/4@1
+
+
+ #26676b
+ #26676b
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal5_Dummy 81/4@1
+
+
+ #60fe08
+ #60fe08
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MetalTop_Dummy 53/4@1
+
+
+ #4665c7
+ #4665c7
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ COMP_Label 22/10@1
+
+
+ #fb1879
+ #fb1879
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Poly2_Label 30/10@1
+
+
+ #880f5b
+ #880f5b
+ 0
+ 0
+ I9
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal1_Label 34/10@1
+
+
+ #ff80b7
+ #ff80b7
+ 0
+ 0
+ I5
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal2_Label 36/10@1
+
+
+ #ad9348
+ #ad9348
+ 0
+ 0
+ I9
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal3_Label 42/10@1
+
+
+ #876bbe
+ #876bbe
+ 0
+ 0
+ I5
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal4_Label 46/10@1
+
+
+ #43e12f
+ #43e12f
+ 0
+ 0
+ I9
+
+ true
+ false
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal5_Label 81/10@1
+
+
+ #a6ec0e
+ #a6ec0e
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MetalTop_Label 53/10@1
+
+
+ #271fe3
+ #271fe3
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal1_Slot 34/3@1
+
+
+ #d56ff8
+ #d56ff8
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal2_Slot 36/3@1
+
+
+ #d8c178
+ #d8c178
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal3_Slot 42/3@1
+
+
+ #6c36fb
+ #6c36fb
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal4_Slot 46/3@1
+
+
+ #225c09
+ #225c09
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal5_Slot 81/3@1
+
+
+ #03f979
+ #03f979
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MetalTop_Slot 53/3@1
+
+
+ #6c9be2
+ #6c9be2
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ UBMPPeri 183/0@1
+
+
+ #0174b8
+ #0174b8
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ UBMPArray 184/0@1
+
+
+ #5bdc70
+ #5bdc70
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ UBMEPlate 185/0@1
+
+
+ #94d628
+ #94d628
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Schottky_diode 241/0@1
+
+
+ #5660ce
+ #5660ce
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ ZENER 178/0@1
+
+
+ #4a0c51
+ #4a0c51
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ RES_MK 110/5@1
+
+
+ #84fd79
+ #84fd79
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ OPC_drc 124/5@1
+
+
+ #49ee98
+ #49ee98
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ NDMY 111/5@1
+
+
+ #0c7e5b
+ #0c7e5b
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ PMNDMY 152/5@1
+
+
+ #49b403
+ #49b403
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ V5_XTOR 112/1@1
+
+
+ #2522b7
+ #2522b7
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ CAP_MK 117/5@1
+
+
+ #0c5e62
+ #0c5e62
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MOS_CAP_MK 166/5@1
+
+
+ #99ac7e
+ #99ac7e
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ IND_MK 151/5@1
+
+
+ #9f0f89
+ #9f0f89
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ DIODE_MK 115/5@1
+
+
+ #04507b
+ #04507b
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ DRC_BJT 127/5@1
+
+
+ #0e7575
+ #0e7575
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ LVS_BJT 118/5@1
+
+
+ #f6b1ef
+ #f6b1ef
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MIM_L_MK 117/10@1
+
+
+ #10c9d8
+ #10c9d8
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Latchup_MK 137/5@1
+
+
+ #79d94d
+ #79d94d
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ GUARD_RING_MK 167/5@1
+
+
+ #2aa0fb
+ #2aa0fb
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ OTP_MK 173/5@1
+
+
+ #7b407b
+ #7b407b
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MTPMARK 122/5@1
+
+
+ #f894c4
+ #f894c4
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ NEO_EE_MK 88/17@1
+
+
+ #d69c01
+ #d69c01
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ SramCore 108/5@1
+
+
+ #c80a86
+ #c80a86
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ LVS_RF 100/5@1
+
+
+ #fa898a
+ #fa898a
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ LVS_Drain 100/7@1
+
+
+ #5a305a
+ #5a305a
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ IND_MK 151/5@1
+
+
+ #c2bf6d
+ #c2bf6d
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ HVPOLYRS 123/5@1
+
+
+ #9f3b8a
+ #9f3b8a
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ LVS_IO 119/5@1
+
+
+ #5aac8a
+ #5aac8a
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ PROBE_MK 13/17@1
+
+
+ #5de533
+ #5de533
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ ESD_MK 24/5@1
+
+
+ #b654a3
+ #b654a3
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ LVS_Source 100/8@1
+
+
+ #9beaaf
+ #9beaaf
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ WELL_DIODE_MK 153/51@1
+
+
+ #b0eb32
+ #b0eb32
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ LDMOS_XTOR 226/0@1
+
+
+ #2507df
+ #2507df
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ PLFUSE 125/5@1
+
+
+ #7791b3
+ #7791b3
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ EFUSE_MK 80/5@1
+
+
+ #ac9801
+ #ac9801
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MCELL_FEOL_MK 11/17@1
+
+
+ #ae438e
+ #ae438e
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ YMTP_MK 86/17@1
+
+
+ #5779b7
+ #5779b7
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ DEV_WF_MK 128/17@1
+
+
+ #8e3415
+ #8e3415
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal1_BLK 34/5@1
+
+
+ #47649f
+ #47649f
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal2_BLK 36/5@1
+
+
+ #3bf37a
+ #3bf37a
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal3_BLK 42/5@1
+
+
+ #678619
+ #678619
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal4_BLK 46/5@1
+
+
+ #44fa82
+ #44fa82
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal5_BLK 81/5@1
+
+
+ #614b6a
+ #614b6a
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MetalT_BLK 53/5@1
+
+
+ #d9f817
+ #d9f817
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ PR_bndry 0/0@1
+
+
+ #0bdfb7
+ #0bdfb7
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ MDIODE 116/5@1
+
+
+ #658af3
+ #658af3
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal1_Res 110/11@1
+
+
+ #e9465c
+ #e9465c
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal2_Res 110/12@1
+
+
+ #ba3263
+ #ba3263
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal3_Res 110/13@1
+
+
+ #ddeef3
+ #ddeef3
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal4_Res 110/14@1
+
+
+ #004676
+ #004676
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal5_Res 110/15@1
+
+
+ #e4b00d
+ #e4b00d
+ 0
+ 0
+ I5
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Metal6_Res 110/16@1
+
+
+ #edeb06
+ #edeb06
+ 0
+ 0
+ I9
+
+ true
+ true
+ false
+ 1
+ false
+ false
+ 0
+
+ Border 63/0@1
+
+
+
diff --git a/technology/gf180mcu/tech/gf180mcu.lyt b/technology/gf180mcu/tech/gf180mcu.lyt
new file mode 100644
index 00000000..c5959dcd
--- /dev/null
+++ b/technology/gf180mcu/tech/gf180mcu.lyt
@@ -0,0 +1,233 @@
+
+
+
+ gf180mcu
+ GLOBALFOUNDRIES 0.18UM 3.3V/(5V)6V MCU TECHNOLOGY
+
+ 0.001
+
+ $PDK_ROOT/$PDK/libs.tech/klayout
+ gf180mcu.lyp
+ true
+
+
+ 1
+ true
+ true
+
+
+ true
+ layer_map()
+ true
+ true
+
+
+ true
+ layer_map()
+ 0.001
+ true
+ #1
+ true
+ #1
+ false
+ #1
+ true
+ OUTLINE
+ true
+ PLACEMENT_BLK
+ true
+ REGIONS
+ true
+
+ 0
+ true
+ .PIN
+ 2
+ true
+ .PIN
+ 2
+ true
+ .FILL
+ 5
+ true
+ .OBS
+ 3
+ true
+ .BLK
+ 4
+ true
+ .LABEL
+ 1
+ true
+ .LABEL
+ 1
+ true
+
+ 0
+ true
+
+ 0
+ VIA_
+ true
+ default
+ false
+
+
+
+ false
+ true
+ true
+ 64
+ 0
+ 1
+ 0
+ DATA
+ 0
+ 0
+ BORDER
+ layer_map()
+ true
+
+
+ 0.001
+ 1
+ 100
+ 100
+ 0
+ 0
+ 0
+ false
+ false
+ false
+ true
+ layer_map()
+
+
+ 0
+ 0.001
+ layer_map()
+ true
+ false
+
+
+ 1
+ 0.001
+ layer_map()
+ true
+ false
+ true
+
+
+
+
+
+
+ true
+ false
+ false
+ false
+ false
+ false
+ 8000
+ 32000
+ LIB
+
+
+ 2
+ false
+ false
+ 1
+ *
+ false
+
+
+ 0
+
+
+ false
+ false
+
+
+ 0
+
+ true
+
+
+
+ # Provide z stack information here
+#
+# Each line is one layer. The specification consists of a layer specification, a colon and arguments.
+# The arguments are named (like "x=...") or in serial. Parameters are separated by comma or blanks.
+# Named arguments are:
+#
+# zstart The lower z position of the extruded layer in µm
+# zstop The upper z position of the extruded layer in µm
+# height The height of the extruded layer in µm
+#
+# 'height', 'zstart' and 'zstop' can be used in any combination. If no value is given for 'zstart',
+# the upper level of the previous layer will be used.
+#
+# If a single unnamed parameter is given, it corresponds to 'height'. Two parameters correspond to
+# 'zstart' and 'zstop'.
+#
+# Examples:
+# 1: 0.5 1.5 # extrude layer 1/0 from 0.5 to 1.5 vertically
+# 1/0: 0.5 1.5 # same with explicit datatype
+# 1: zstop=1.5, zstart=0.5 # same with named parameters
+# 1: height=1.0, zstop=1.5 # same with z stop minus height
+# 1: 1.0 zstop=1.5 # same with height as unnamed parameter
+#
+# VARIABLES
+#
+# You can declare variables with:
+# var name = value
+#
+# You can use variables inside numeric expressions.
+# Example:
+# var hmetal = 0.48
+# 7/0: 0.5 0.5+hmetal*2 # 2x thick metal
+#
+# You cannot use variables inside layer specifications currently.
+#
+# CONDITIONALS
+#
+# You can enable or disable branches of the table using 'if', 'else', 'elseif' and 'end':
+# Example:
+# var thick_m1 = true
+# if thickm1
+# 1: 0.5 1.5
+# else
+# 1: 0.5 1.2
+# end
+
+
+
+
+ 30/0,33/0,Metal1
+ Metal1,35/0,Metal2
+ Metal2,38/0,Metal3
+ Metal3,40/0,Metal4
+ Metal4,41/0,Metal5
+ Metal5,82/0,53/0
+
+ Metal1='34/0+34/10'
+ Metal2='36/0+36/10'
+ Metal3='42/0+42/10'
+ Metal4='46/0+46/10'
+ Metal5='81/0+81/10'
+
+
diff --git a/technology/gf180mcu/tech/tech.py b/technology/gf180mcu/tech/tech.py
new file mode 100644
index 00000000..ed54979c
--- /dev/null
+++ b/technology/gf180mcu/tech/tech.py
@@ -0,0 +1,482 @@
+# See LICENSE for licensing information.
+#
+# Copyright (c) 2016-2021 Regents of the University of California and The Board
+# of Regents for the Oklahoma Agricultural and Mechanical College
+# (acting for and on behalf of Oklahoma State University)
+# All rights reserved.
+#
+import os
+from openram import drc as d
+
+"""
+File containing the process technology parameters for Global Foundaries 180nm
+"""
+
+###################################################
+# Custom modules
+###################################################
+
+# This uses the default classes to instantiate module from
+# '$OPENRAM_HOME/compiler/modules'.
+# Using tech_modules['cellname'] you can override each class by providing a custom
+# implementation in '$OPENRAM_TECHDIR/modules/'
+# For example: tech_modules['contact'] = 'contact_scn4m'
+tech_modules = d.module_type()
+
+tech_modules["bitcell_1port"] = "gf180_bitcell"
+tech_modules["nand2_dec"] = "nand2_dec"
+
+###################################################
+# Custom cell properties
+###################################################
+cell_properties = d.cell_properties()
+
+# is there a better way to tell if the user overrode the port order than this?
+# this is needed to correctly create the bitcell_pins list in the bitcell_base_array
+cell_properties.override_bitcell_1port_order = True
+cell_properties.bitcell_1port.port_order = ['bl', 'br', 'gnd', 'vdd', 'vpb', 'vnb', 'wl']
+cell_properties.bitcell_1port.port_types = ["OUTPUT", "OUTPUT", "GROUND", "POWER", "BIAS", "BIAS", "INPUT"]
+cell_properties.bitcell_1port.port_map = {'bl': 'BL',
+ 'br': 'BR',
+ 'wl': 'WL',
+ 'vdd': 'VDD',
+ 'vnb': 'pwell',
+ 'vpb': 'nwell',
+ 'gnd': 'GND'}
+
+cell_properties.bitcell_1port.wl_layer = "m3"
+cell_properties.bitcell_1port.bl_layer = "m2"
+cell_properties.bitcell_1port.vdd_layer = "m1"
+cell_properties.bitcell_1port.gnd_layer = "m1"
+
+cell_properties.nand2_dec.port_order = ['A', 'B', 'Z', 'vdd', 'gnd']
+cell_properties.nand2_dec.port_map = {'A': 'A',
+ 'B': 'B',
+ 'Z': 'Z',
+ 'vdd': 'VDD',
+ 'gnd': 'GND'}
+
+
+cell_properties.ptx.model_is_subckt = True
+
+cell_properties.strap_placement = 8 # this means strap cell gets placed after every 8 bitcells
+
+cell_properties.names["nand2_dec"] = ["gf180mcu_3v3__nand2_1_dec"]
+
+###################################################
+# Custom layer properties
+###################################################
+layer_properties = d.layer_properties()
+
+###################################################
+# GDS file info
+###################################################
+GDS={}
+# gds units
+# From http://www.cnf.cornell.edu/cnf_spie9.html: "The first
+#is the size of a database unit in user units. The second is the size
+#of a database unit in meters. For example, if your library was
+#created with the default units (user unit = 1 m and 1000 database
+#units per user unit), then the first number would be 0.001 and the
+#second number would be 10-9. Typically, the first number is less than
+#1, since you use more than 1 database unit per user unit. To
+#calculate the size of a user unit in meters, divide the second number
+#by the first."
+GDS["unit"]=(0.001,1e-6)
+# default label zoom
+GDS["zoom"] = 0.5
+
+###################################################
+# Interconnect stacks
+###################################################
+
+poly_stack = ("poly", "contact", "m1")
+active_stack = ("active", "contact", "m1")
+m1_stack = ("m1", "via1", "m2")
+m2_stack = ("m2", "via2", "m3")
+m3_stack = ("m3", "via3", "m4")
+m4_stack = ("m4", "via4", "m5")
+
+
+layer_indices = {"poly": 0,
+ "active": 0,
+ "nwell": 0,
+ "pwell": 0,
+ "m1": 1,
+ "m2": 2,
+ "m3": 3,
+ "m4": 4,
+ "m5": 5}
+
+# The FEOL stacks get us up to m1
+feol_stacks = [poly_stack,
+ active_stack]
+
+# The BEOL stacks are m1 and up
+beol_stacks = [m1_stack,
+ m2_stack,
+ m3_stack,
+ m4_stack]
+
+layer_stacks = feol_stacks + beol_stacks
+
+preferred_directions = {"poly": "V",
+ "active": "V",
+ "m1": "V",
+ "m2": "H",
+ "m3": "V",
+ "m4": "H",
+ "m5": "V"}
+
+###################################################
+# Power grid
+###################################################
+# Use M3/M4
+power_grid = m4_stack
+
+###################################################
+##GDS Layer Map
+###################################################
+
+# create the GDS layer map
+layer={}
+layer["pwell"] = (204, 0)
+layer["nwell"] = (21, 0)
+layer["dnwell"] = (12, 0)
+layer["active"] = (22, 0)
+layer["pimplant"] = (31, 0)
+layer["nimplant"] = (32, 0)
+layer["poly"] = (30, 0)
+layer["contact"] = (33, 0)
+layer["m1"] = (34, 0)
+layer["via1"] = (35, 0)
+layer["m2"] = (36, 0)
+layer["via2"] = (38, 0)
+layer["m3"] = (42, 0)
+layer["via3"] = (40, 0)
+layer["m4"] = (46, 0)
+layer["via4"] = (41, 0)
+layer["m5"] = (81, 0)
+# Not an official layer
+layer["text"] = (234, 5)
+layer["mem"] = (108, 5)
+layer["boundary"] = (0, 0)
+
+label_purpose = 10
+#use_purpose = {}
+
+# Layer names for external PDKs
+layer_names = {}
+layer_names["active"] = "active"
+layer_names["pwell"] = "pwell"
+layer_names["nwell"] = "nwell"
+layer_names["dnwell"] = "dnwell"
+layer_names["nimplant"]= "nimplant"
+layer_names["pimplant"]= "pimplant"
+layer_names["poly"] = "poly"
+layer_names["contact"] = "contact"
+layer_names["m1"] = "metal1"
+layer_names["via1"] = "via1"
+layer_names["m2"] = "metal2"
+layer_names["via2"] = "via2"
+layer_names["m3"] = "metal3"
+layer_names["via3"] = "via3"
+layer_names["m4"] = "metal4"
+layer_names["text"] = "text"
+layer_names["mem"] = "SramCore"
+layer_names["boundary"]= "boundary"
+
+###################################################
+# DRC/LVS Rules Setup
+###################################################
+
+# technology parameter
+parameter={}
+
+parameter["min_tx_size"] = 0.250
+parameter["beta"] = 3
+
+parameter["6T_inv_nmos_size"] = 0.6
+parameter["6T_inv_pmos_size"] = 0.95
+parameter["6T_access_size"] = 0.6
+drc = d.design_rules("gf180")
+
+# grid size
+drc["grid"] = 0.005
+
+# minwidth_tx with contact (no dog bone transistors)
+drc["minwidth_tx"] = 0.57
+# PL.2 Min gate width/channel length for 3V3 pmos
+drc["minlength_channel"] = 0.28
+
+drc["minlength_channel_pmos"] = 0.55
+drc["minlength_channel_nmos"] = 0.7
+
+drc["pwell_to_nwell"] = 0 # assuming same potential
+
+drc.add_layer("nwell",
+ width=0.86,
+ spacing=0.6)
+
+drc.add_layer("pwell",
+ width=0.74, # 0.6 for 3.3v
+ spacing=0.86) # equal potential
+
+# PL.1 minwidth of interconnect poly 3v3
+# PL.3a poly spacing 3v3
+drc.add_layer("poly",
+ width=0.28,
+ spacing=0.24)
+
+drc["poly_extend_active"] = 0.22
+
+drc["poly_to_contact"] = 0
+
+#drc["active_enclose_gate"] = 0.075
+
+drc["poly_to_active"] = 0.1
+
+#drc["poly_to_field_poly"] = 0.210
+
+#
+# DF.1a - minwidth of active (3v3)
+# min space of tap to diff across butted junction
+# DF.9 - minarea of active area=0.2025
+drc.add_layer("active",
+ width=0.22,
+ spacing=0.33)
+
+drc.add_enclosure("dnwell",
+ layer="pwell",
+ enclosure=2.5,
+ extension=2.5)
+
+drc.add_enclosure("nwell",
+ layer="active",
+ enclosure=0.43,
+ extension=0.6)
+
+drc.add_enclosure("pwell",
+ layer="active",
+ enclosure=0.43,
+ extension=0.6)
+
+drc.add_enclosure("implant",
+ layer="active",
+ enclosure=0.125)
+
+# Same as active enclosure?
+#drc["implant_to_contact"] = 0.070
+
+drc.add_layer("implant",
+ width=0.4,
+ spacing=0.4,
+ area=0.35)
+
+drc.add_layer("contact",
+ width=0.22,
+ spacing=0.25)
+# CO.4 - active enclosure of contact
+# extension is not a true drc rule, used to extend active to reach active min area
+drc.add_enclosure("active",
+ layer="contact",
+ enclosure=0.07,
+ extension=0.175)
+
+drc.add_enclosure("poly",
+ layer="contact",
+ enclosure=0.07,
+ extension=0.07)
+
+drc["active_contact_to_gate"] = 0.15
+
+drc["poly_contact_to_gate"] = 0.165
+
+#drc["npc_enclose_poly"] = 0.1
+
+# M1.1 - width
+# M1.2a - space
+# M1.3 - area
+drc.add_layer("m1",
+ width=0.26,
+ spacing=0.23)
+
+drc.add_enclosure("m1",
+ layer="contact",
+ enclosure=0,
+ extension=0.205)
+
+drc.add_enclosure("m1",
+ layer="via1",
+ enclosure=0,
+ extension=0.15)
+
+drc.add_layer("via1",
+ width=0.26,
+ spacing=0.26)
+
+drc.add_layer("m2",
+ width=0.28,
+ spacing=0.28,
+ area=0.1444)
+
+drc.add_enclosure("m2",
+ layer="via1",
+ enclosure=0.06,
+ extension=0.06)
+
+drc.add_enclosure("m2",
+ layer="via2",
+ enclosure=0.06,
+ extension=0.06)
+
+drc.add_layer("via2",
+ width=0.26,
+ spacing=0.26)
+
+drc.add_layer("m3",
+ width=0.28,
+ spacing=0.28,
+ area=0.1444)
+
+drc.add_enclosure("m3",
+ layer="via2",
+ enclosure=0.06)
+
+
+drc.add_enclosure("m3",
+ layer="via3",
+ enclosure=0.06,
+ extension=0.06)
+
+drc.add_layer("via3",
+ width=0.26,
+ spacing=0.26)
+
+drc.add_layer("m4",
+ width=0.28,
+ spacing=0.28,
+ area=0.1444)
+
+drc.add_enclosure("m4",
+ layer="via3",
+ enclosure=0.06)
+
+drc.add_enclosure("m4",
+ layer="via4",
+ enclosure=0.06)
+
+drc.add_layer("via4",
+ width=0.26,
+ spacing=0.26)
+
+# Magic wants 0.36um width but PDK says 0.28
+drc.add_layer("m5",
+ width=0.36,
+ spacing=0.28,
+ area=0.1444)
+
+drc.add_enclosure("m5",
+ layer="via4",
+ enclosure=0.06)
+
+drc.add_enclosure("m5",
+ layer="via5",
+ enclosure=0.06)
+
+drc.add_layer("via5",
+ width=0.26,
+ spacing=0.26)
+
+# m5.1 Minimum width of metal5
+# m5.2 Minimum spacing of metal5
+# m5.7 Minimum area of metal5
+#drc.add_layer("m5",
+# width=1.600,
+# spacing=1.600,
+# area=4.000)
+# m5.3 Minimum enclosure around via4
+#drc.add_enclosure("m5",
+# layer="via4",
+# enclosure=0.310)
+
+
+
+# Metal 5-10 are ommitted
+
+###################################################
+# Spice Simulation Parameters
+###################################################
+
+# spice info
+spice = {}
+spice["nmos"] = "nfet_03v3"
+spice["pmos"] = "pfet_03v3"
+spice["power"]="vccd1"
+spice["ground"]="vssd1"
+
+spice["fet_libraries"] = {"TT": [[os.environ.get("SPICE_MODEL_DIR") + "/sm141064.ngspice", "typical"]]}
+
+# spice stimulus related variables
+spice["feasible_period"] = 10 # estimated feasible period in ns
+spice["supply_voltages"] = [1.7, 1.8, 1.9] # Supply voltage corners in [Volts]
+spice["nom_supply_voltage"] = 1.8 # Nominal supply voltage in [Volts]
+spice["rise_time"] = 0.005 # rise time in [Nano-seconds]
+spice["fall_time"] = 0.005 # fall time in [Nano-seconds]
+spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius)
+spice["nom_temperature"] = 25 # Nominal temperature (celcius)
+
+# analytical delay parameters
+spice["nom_threshold"] = 0.49 # Typical Threshold voltage in Volts
+spice["wire_unit_r"] = 0.125 # Unit wire resistance in ohms/square
+spice["wire_unit_c"] = 0.134 # Unit wire capacitance ff/um^2
+spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff
+spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff
+spice["dff_setup"] = 102.5391 # DFF setup time in ps
+spice["dff_hold"] = -56 # DFF hold time in ps
+spice["dff_in_cap"] = 6.89 # Input capacitance (D) [Femto-farad]
+spice["dff_out_cap"] = 6.89 # Output capacitance (Q) [Femto-farad]
+
+# analytical power parameters, many values are temporary
+spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW
+spice["inv_leakage"] = 1 # Leakage power of inverter in nW
+spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW
+spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW
+spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW
+spice["dff_leakage"] = 1 # Leakage power of flop in nW
+
+spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz
+
+# Parameters related to sense amp enable timing and delay chain/RBL sizing
+parameter["le_tau"] = 2.25 # In pico-seconds.
+parameter["cap_relative_per_ff"] = 7.5 # Units of Relative Capacitance/ Femto-Farad
+parameter["dff_clk_cin"] = 30.6 # relative capacitance
+parameter["6tcell_wl_cin"] = 3 # relative capacitance
+parameter["min_inv_para_delay"] = 2.4 # Tau delay units
+parameter["sa_en_pmos_size"] = 0.72 # micro-meters
+parameter["sa_en_nmos_size"] = 0.27 # micro-meters
+parameter["sa_inv_pmos_size"] = 0.54 # micro-meters
+parameter["sa_inv_nmos_size"] = 0.27 # micro-meters
+parameter["bitcell_drain_cap"] = 0.1 # In Femto-Farad, approximation of drain capacitance
+
+###################################################
+# Technology Tool Preferences
+###################################################
+
+#if use_calibre:
+# drc_name = "calibre"
+# lvs_name = "calibre"
+# pex_name = "calibre"
+#if use_klayout:
+# drc_name = "klayout"
+# lvs_name = "klayout"
+# pex_name = "klayout"
+#else:
+drc_name = "magic"
+lvs_name = "netgen"
+pex_name = "magic"
+
+
+flatglob = ["*_?mos_m*"]
+
+ignore_drc_lvs_on = ["wl_strap"]
diff --git a/technology/sky130/tech/tech.py b/technology/sky130/tech/tech.py
index 9c491013..c8c642a8 100755
--- a/technology/sky130/tech/tech.py
+++ b/technology/sky130/tech/tech.py
@@ -114,15 +114,15 @@ cell_properties.bitcell_2port.vdd_dir = "H"
cell_properties.bitcell_2port.gnd_layer = "m2"
cell_properties.bitcell_2port.gnd_dir = "H"
-cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'vdd', 'gnd', 'br', 'gate', 'vpb', 'vnb'],
- ['INPUT', 'POWER', 'GROUND', 'INPUT', 'INPUT', 'BIAS', 'BIAS'],
+cell_properties.col_cap_1port_bitcell = d.cell(['bl', 'br', 'vdd', 'gnd', 'vpb', 'vnb', 'gate'],
+ ['INPUT', 'INPUT','POWER', 'GROUND', 'BIAS', 'BIAS', 'INPUT'],
{'bl': 'bl',
'br': 'br',
'vdd': 'vdd',
'gnd': 'gnd',
- 'gate': 'gate',
'vnb': 'vnb',
- 'vpb': 'vpb'})
+ 'vpb': 'vpb',
+ 'gate': 'gate'})
cell_properties.col_cap_1port_bitcell.boundary_layer = "mem"
cell_properties.col_cap_1port_strap_power = d.cell(['vdd', 'vpb', 'vnb'],
@@ -415,8 +415,8 @@ label_purpose = 5
# pin_read purposes
special_purposes = {layer["nwell"][0]: [layer["nwell"][1], 5, 59, 16]}
#layer_override = {"VNB\x00": ["pwell",122]}
-layer_override = {"VNB": layer["pwellp"]}
-layer_override_name = {"VNB": "pwellp"}
+layer_override = {"vnb": layer["pwellp"], "VNB": layer["pwellp"]}
+layer_override_name = {"vnb": "pwellp", "VNB": "pwellp"}
layer_override_purpose = {122: (64, 59)}
# Layer names for external PDKs
layer_names = {}