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 = {}