mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into issue_fix
This commit is contained in:
commit
efd43c3191
|
|
@ -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
|
||||
|
|
|
|||
48
Makefile
48
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."
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 = []
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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())
|
||||
|
|
@ -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')
|
||||
|
||||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@
|
|||
|
||||
export OPENRAM_HOME="`pwd`/compiler"
|
||||
export OPENRAM_TECH="`pwd`/technology"
|
||||
export PDK_ROOT="$HOME/gf/pdk"
|
||||
export PYTHONPATH=$OPENRAM_HOME
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
@ -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.
Binary file not shown.
|
|
@ -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}
|
||||
|
|
@ -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 >>
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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}
|
||||
|
|
@ -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 *
|
||||
|
|
@ -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
|
|
@ -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>
|
||||
|
|
@ -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"]
|
||||
|
|
@ -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 = {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue