Merge branch 'dev' into issue_fix

This commit is contained in:
Eren Dogan 2023-12-06 13:30:22 -08:00
commit efd43c3191
43 changed files with 3607 additions and 186 deletions

5
.gitignore vendored
View File

@ -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

View File

@ -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."

View File

@ -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!

View File

@ -1 +1 @@
1.2.40
1.2.42

View File

@ -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,

View File

@ -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,

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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")
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")

View File

@ -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",

View File

@ -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 = []

View File

@ -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)

View File

@ -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())

View File

@ -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):

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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())

View File

@ -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())

View File

@ -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')

View File

@ -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 \

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -110,7 +110,7 @@ Commercial tools (optional):
* Michael Grimes
* Jennifer Sowash
* Jesse Cirimelli-Low
<img align="right" height="100" src="../assets/images/logos/vlsida.png">
<img align="right" height="100" src="../assets/images/logos/vlsida.png">https://www.youtube.com/watch?v=rd5j8mG24H4&t=0s
* Many other past students:
* Jeff Butera
* Tom Golubev

View File

@ -6,4 +6,5 @@
export OPENRAM_HOME="`pwd`/compiler"
export OPENRAM_TECH="`pwd`/technology"
export PDK_ROOT="$HOME/gf/pdk"
export PYTHONPATH=$OPENRAM_HOME

View File

@ -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

View File

@ -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)

Binary file not shown.

View File

@ -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}

View File

@ -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 >>

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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 *

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2022 GlobalFoundries PDK Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<klayout-macro>
<description/>
<version/>
<category>pymacros</category>
<prolog/>
<epilog/>
<doc/>
<autorun>true</autorun>
<autorun-early>false</autorun-early>
<shortcut/>
<show-in-menu>false</show-in-menu>
<group-name/>
<menu-path/>
<interpreter>python</interpreter>
<dsl-interpreter-name/>
<text>
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)
</text>
</klayout-macro>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,233 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2022 GlobalFoundries PDK Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<technology>
<name>gf180mcu</name>
<description>GLOBALFOUNDRIES 0.18UM 3.3V/(5V)6V MCU TECHNOLOGY</description>
<group/>
<dbu>0.001</dbu>
<base-path/>
<original-base-path>$PDK_ROOT/$PDK/libs.tech/klayout</original-base-path>
<layer-properties_file>gf180mcu.lyp</layer-properties_file>
<add-other-layers>true</add-other-layers>
<reader-options>
<gds2>
<box-mode>1</box-mode>
<allow-big-records>true</allow-big-records>
<allow-multi-xy-records>true</allow-multi-xy-records>
</gds2>
<common>
<create-other-layers>true</create-other-layers>
<layer-map>layer_map()</layer-map>
<enable-properties>true</enable-properties>
<enable-text-objects>true</enable-text-objects>
</common>
<lefdef>
<read-all-layers>true</read-all-layers>
<layer-map>layer_map()</layer-map>
<dbu>0.001</dbu>
<produce-net-names>true</produce-net-names>
<net-property-name>#1</net-property-name>
<produce-inst-names>true</produce-inst-names>
<inst-property-name>#1</inst-property-name>
<produce-pin-names>false</produce-pin-names>
<pin-property-name>#1</pin-property-name>
<produce-cell-outlines>true</produce-cell-outlines>
<cell-outline-layer>OUTLINE</cell-outline-layer>
<produce-placement-blockages>true</produce-placement-blockages>
<placement-blockage-layer>PLACEMENT_BLK</placement-blockage-layer>
<produce-regions>true</produce-regions>
<region-layer>REGIONS</region-layer>
<produce-via-geometry>true</produce-via-geometry>
<via_geometry-suffix-string/>
<via_geometry-datatype-string>0</via_geometry-datatype-string>
<produce-pins>true</produce-pins>
<pins-suffix-string>.PIN</pins-suffix-string>
<pins-datatype-string>2</pins-datatype-string>
<produce-lef-pins>true</produce-lef-pins>
<lef_pins-suffix-string>.PIN</lef_pins-suffix-string>
<lef_pins-datatype-string>2</lef_pins-datatype-string>
<produce-fills>true</produce-fills>
<fills-suffix-string>.FILL</fills-suffix-string>
<fills-datatype-string>5</fills-datatype-string>
<produce-obstructions>true</produce-obstructions>
<obstructions-suffix>.OBS</obstructions-suffix>
<obstructions-datatype>3</obstructions-datatype>
<produce-blockages>true</produce-blockages>
<blockages-suffix>.BLK</blockages-suffix>
<blockages-datatype>4</blockages-datatype>
<produce-labels>true</produce-labels>
<labels-suffix>.LABEL</labels-suffix>
<labels-datatype>1</labels-datatype>
<produce-lef-labels>true</produce-lef-labels>
<lef-labels-suffix>.LABEL</lef-labels-suffix>
<lef-labels-datatype>1</lef-labels-datatype>
<produce-routing>true</produce-routing>
<routing-suffix-string/>
<routing-datatype-string>0</routing-datatype-string>
<produce-special-routing>true</produce-special-routing>
<special-routing-suffix-string/>
<special-routing-datatype-string>0</special-routing-datatype-string>
<via-cellname-prefix>VIA_</via-cellname-prefix>
<read-lef-with-def>true</read-lef-with-def>
<macro-resolution-mode>default</macro-resolution-mode>
<separate-groups>false</separate-groups>
<map-file/>
</lefdef>
<mebes>
<invert>false</invert>
<subresolution>true</subresolution>
<produce-boundary>true</produce-boundary>
<num-stripes-per-cell>64</num-stripes-per-cell>
<num-shapes-per-cell>0</num-shapes-per-cell>
<data-layer>1</data-layer>
<data-datatype>0</data-datatype>
<data-name>DATA</data-name>
<boundary-layer>0</boundary-layer>
<boundary-datatype>0</boundary-datatype>
<boundary-name>BORDER</boundary-name>
<layer-map>layer_map()</layer-map>
<create-other-layers>true</create-other-layers>
</mebes>
<dxf>
<dbu>0.001</dbu>
<unit>1</unit>
<text-scaling>100</text-scaling>
<circle-points>100</circle-points>
<circle-accuracy>0</circle-accuracy>
<contour-accuracy>0</contour-accuracy>
<polyline-mode>0</polyline-mode>
<render-texts-as-polygons>false</render-texts-as-polygons>
<keep-other-cells>false</keep-other-cells>
<keep-layer-names>false</keep-layer-names>
<create-other-layers>true</create-other-layers>
<layer-map>layer_map()</layer-map>
</dxf>
<cif>
<wire-mode>0</wire-mode>
<dbu>0.001</dbu>
<layer-map>layer_map()</layer-map>
<create-other-layers>true</create-other-layers>
<keep-layer-names>false</keep-layer-names>
</cif>
<mag>
<lambda>1</lambda>
<dbu>0.001</dbu>
<layer-map>layer_map()</layer-map>
<create-other-layers>true</create-other-layers>
<keep-layer-names>false</keep-layer-names>
<merge>true</merge>
<lib-paths>
</lib-paths>
</mag>
</reader-options>
<writer-options>
<gds2>
<write-timestamps>true</write-timestamps>
<write-cell-properties>false</write-cell-properties>
<write-file-properties>false</write-file-properties>
<no-zero-length-paths>false</no-zero-length-paths>
<multi-xy-records>false</multi-xy-records>
<resolve-skew-arrays>false</resolve-skew-arrays>
<max-vertex-count>8000</max-vertex-count>
<max-cellname-length>32000</max-cellname-length>
<libname>LIB</libname>
</gds2>
<oasis>
<compression-level>2</compression-level>
<write-cblocks>false</write-cblocks>
<strict-mode>false</strict-mode>
<write-std-properties>1</write-std-properties>
<subst-char>*</subst-char>
<permissive>false</permissive>
</oasis>
<cif>
<polygon-mode>0</polygon-mode>
</cif>
<cif>
<dummy-calls>false</dummy-calls>
<blank-separator>false</blank-separator>
</cif>
<mag>
<lambda>0</lambda>
<tech/>
<write-timestamp>true</write-timestamp>
</mag>
</writer-options>
<d25>
<src># 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
</src>
</d25>
<connectivity>
<connection>30/0,33/0,Metal1</connection>
<connection>Metal1,35/0,Metal2</connection>
<connection>Metal2,38/0,Metal3</connection>
<connection>Metal3,40/0,Metal4</connection>
<connection>Metal4,41/0,Metal5</connection>
<connection>Metal5,82/0,53/0</connection>
<symbols>Metal1='34/0+34/10'</symbols>
<symbols>Metal2='36/0+36/10'</symbols>
<symbols>Metal3='42/0+42/10'</symbols>
<symbols>Metal4='46/0+46/10'</symbols>
<symbols>Metal5='81/0+81/10'</symbols>
</connectivity>
</technology>

View File

@ -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"]

View File

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