2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2018-07-10 23:17:09 +02:00
|
|
|
from hierarchy_design import hierarchy_design
|
2020-11-14 00:55:55 +01:00
|
|
|
import utils
|
2018-07-10 00:43:26 +02:00
|
|
|
import contact
|
2020-11-14 00:55:55 +01:00
|
|
|
from tech import GDS, layer
|
2020-10-27 17:23:11 +01:00
|
|
|
from tech import preferred_directions
|
2020-11-03 16:06:01 +01:00
|
|
|
from tech import cell_properties as props
|
2018-07-10 00:43:26 +02:00
|
|
|
from globals import OPTS
|
2019-12-17 20:03:36 +01:00
|
|
|
import re
|
2020-11-03 16:06:01 +01:00
|
|
|
import debug
|
2019-10-03 01:26:02 +02:00
|
|
|
|
2019-12-18 00:07:01 +01:00
|
|
|
|
2018-07-10 23:17:09 +02:00
|
|
|
class design(hierarchy_design):
|
2018-07-10 00:43:26 +02:00
|
|
|
"""
|
|
|
|
|
This is the same as the hierarchy_design class except it contains
|
2019-12-13 23:13:41 +01:00
|
|
|
some DRC/layer constants and analytical models for other modules to reuse.
|
2018-07-10 00:43:26 +02:00
|
|
|
"""
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
def __init__(self, name, cell_name=None, prop=None):
|
2020-11-03 16:06:01 +01:00
|
|
|
# This allows us to use different GDS/spice circuits for hard cells instead of the default ones
|
|
|
|
|
# Except bitcell names are generated automatically by the globals.py setup_bitcells routines
|
|
|
|
|
# depending on the number of ports.
|
|
|
|
|
if name in props.names:
|
|
|
|
|
cell_name = props.names[name]
|
|
|
|
|
elif not cell_name:
|
2020-11-03 01:00:16 +01:00
|
|
|
cell_name = name
|
|
|
|
|
super().__init__(name, cell_name)
|
2018-07-10 00:43:26 +02:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
# This means it is a custom cell.
|
|
|
|
|
# It could have properties and not be a hard cell too (e.g. dff_buf)
|
|
|
|
|
if prop and prop.hard_cell:
|
|
|
|
|
# The pins get added from the spice file
|
|
|
|
|
debug.check(prop.port_names == self.pins,
|
|
|
|
|
"Custom cell pin names do not match spice file:\n{0} vs {1}".format(prop.port_names, self.pins))
|
|
|
|
|
self.add_pin_types(prop.port_types)
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
(width, height) = utils.get_libcell_size(self.cell_name,
|
|
|
|
|
GDS["unit"],
|
|
|
|
|
layer[prop.boundary_layer])
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
self.pin_map = utils.get_libcell_pins(self.pins,
|
|
|
|
|
self.cell_name,
|
|
|
|
|
GDS["unit"])
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-11-14 17:08:42 +01:00
|
|
|
self.width = width
|
|
|
|
|
self.height = height
|
2020-11-14 00:55:55 +01:00
|
|
|
|
2020-10-27 17:28:21 +01:00
|
|
|
self.setup_multiport_constants()
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-09-08 22:31:50 +02:00
|
|
|
def check_pins(self):
|
|
|
|
|
for pin_name in self.pins:
|
|
|
|
|
pins = self.get_pins(pin_name)
|
|
|
|
|
for pin in pins:
|
|
|
|
|
print(pin_name, pin)
|
2020-10-27 17:23:11 +01:00
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def setup_drc_constants(design):
|
|
|
|
|
"""
|
|
|
|
|
These are some DRC constants used in many places
|
|
|
|
|
in the compiler.
|
|
|
|
|
"""
|
|
|
|
|
# Make some local rules for convenience
|
|
|
|
|
from tech import drc
|
|
|
|
|
for rule in drc.keys():
|
|
|
|
|
# Single layer width rules
|
|
|
|
|
match = re.search(r"minwidth_(.*)", rule)
|
|
|
|
|
if match:
|
|
|
|
|
if match.group(1) == "active_contact":
|
|
|
|
|
setattr(design, "contact_width", drc(match.group(0)))
|
|
|
|
|
else:
|
|
|
|
|
setattr(design, match.group(1) + "_width", drc(match.group(0)))
|
|
|
|
|
|
|
|
|
|
# Single layer area rules
|
|
|
|
|
match = re.search(r"minarea_(.*)", rule)
|
|
|
|
|
if match:
|
|
|
|
|
setattr(design, match.group(0), drc(match.group(0)))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-27 17:23:11 +01:00
|
|
|
# Single layer spacing rules
|
|
|
|
|
match = re.search(r"(.*)_to_(.*)", rule)
|
|
|
|
|
if match and match.group(1) == match.group(2):
|
|
|
|
|
setattr(design, match.group(1) + "_space", drc(match.group(0)))
|
|
|
|
|
elif match and match.group(1) != match.group(2):
|
|
|
|
|
if match.group(2) == "poly_active":
|
|
|
|
|
setattr(design, match.group(1) + "_to_contact",
|
|
|
|
|
drc(match.group(0)))
|
|
|
|
|
else:
|
|
|
|
|
setattr(design, match.group(0), drc(match.group(0)))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-27 17:23:11 +01:00
|
|
|
match = re.search(r"(.*)_enclose_(.*)", rule)
|
|
|
|
|
if match:
|
|
|
|
|
setattr(design, match.group(0), drc(match.group(0)))
|
|
|
|
|
|
|
|
|
|
match = re.search(r"(.*)_extend_(.*)", rule)
|
|
|
|
|
if match:
|
|
|
|
|
setattr(design, match.group(0), drc(match.group(0)))
|
|
|
|
|
|
|
|
|
|
# Create the maximum well extend active that gets used
|
|
|
|
|
# by cells to extend the wells for interaction with other cells
|
|
|
|
|
from tech import layer
|
|
|
|
|
design.well_extend_active = 0
|
|
|
|
|
if "nwell" in layer:
|
|
|
|
|
design.well_extend_active = max(design.well_extend_active, design.nwell_extend_active)
|
|
|
|
|
if "pwell" in layer:
|
|
|
|
|
design.well_extend_active = max(design.well_extend_active, design.pwell_extend_active)
|
|
|
|
|
|
|
|
|
|
# The active offset is due to the well extension
|
|
|
|
|
if "pwell" in layer:
|
|
|
|
|
design.pwell_enclose_active = drc("pwell_enclose_active")
|
|
|
|
|
else:
|
|
|
|
|
design.pwell_enclose_active = 0
|
|
|
|
|
if "nwell" in layer:
|
|
|
|
|
design.nwell_enclose_active = drc("nwell_enclose_active")
|
|
|
|
|
else:
|
|
|
|
|
design.nwell_enclose_active = 0
|
|
|
|
|
# Use the max of either so that the poly gates will align properly
|
|
|
|
|
design.well_enclose_active = max(design.pwell_enclose_active,
|
|
|
|
|
design.nwell_enclose_active,
|
|
|
|
|
design.active_space)
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-27 17:23:11 +01:00
|
|
|
# These are for debugging previous manual rules
|
|
|
|
|
if False:
|
|
|
|
|
print("poly_width", design.poly_width)
|
|
|
|
|
print("poly_space", design.poly_space)
|
|
|
|
|
print("m1_width", design.m1_width)
|
|
|
|
|
print("m1_space", design.m1_space)
|
|
|
|
|
print("m2_width", design.m2_width)
|
|
|
|
|
print("m2_space", design.m2_space)
|
|
|
|
|
print("m3_width", design.m3_width)
|
|
|
|
|
print("m3_space", design.m3_space)
|
|
|
|
|
print("m4_width", design.m4_width)
|
|
|
|
|
print("m4_space", design.m4_space)
|
|
|
|
|
print("active_width", design.active_width)
|
|
|
|
|
print("active_space", design.active_space)
|
|
|
|
|
print("contact_width", design.contact_width)
|
|
|
|
|
print("poly_to_active", design.poly_to_active)
|
|
|
|
|
print("poly_extend_active", design.poly_extend_active)
|
|
|
|
|
print("poly_to_contact", design.poly_to_contact)
|
|
|
|
|
print("active_contact_to_gate", design.active_contact_to_gate)
|
|
|
|
|
print("poly_contact_to_gate", design.poly_contact_to_gate)
|
|
|
|
|
print("well_enclose_active", design.well_enclose_active)
|
|
|
|
|
print("implant_enclose_active", design.implant_enclose_active)
|
|
|
|
|
print("implant_space", design.implant_space)
|
|
|
|
|
import sys
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def setup_layer_constants(design):
|
2019-12-18 00:45:07 +01:00
|
|
|
"""
|
2019-12-18 00:07:01 +01:00
|
|
|
These are some layer constants used
|
|
|
|
|
in many places in the compiler.
|
|
|
|
|
"""
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
from tech import layer_indices
|
2019-12-13 23:13:41 +01:00
|
|
|
import tech
|
2020-05-07 21:35:21 +02:00
|
|
|
for layer in layer_indices:
|
|
|
|
|
key = "{}_stack".format(layer)
|
2019-12-18 00:45:07 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
# Set the stack as a local helper
|
|
|
|
|
try:
|
|
|
|
|
layer_stack = getattr(tech, key)
|
2020-10-27 17:23:11 +01:00
|
|
|
setattr(design, key, layer_stack)
|
2020-05-07 21:35:21 +02:00
|
|
|
except AttributeError:
|
|
|
|
|
pass
|
2019-12-18 00:45:07 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
# Skip computing the pitch for active
|
|
|
|
|
if layer == "active":
|
|
|
|
|
continue
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
# Add the pitch
|
2020-10-27 17:23:11 +01:00
|
|
|
setattr(design,
|
2020-05-07 21:35:21 +02:00
|
|
|
"{}_pitch".format(layer),
|
2020-10-27 17:23:11 +01:00
|
|
|
design.compute_pitch(layer, True))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
# Add the non-preferrd pitch (which has vias in the "wrong" way)
|
2020-10-27 17:23:11 +01:00
|
|
|
setattr(design,
|
2020-05-07 21:35:21 +02:00
|
|
|
"{}_nonpref_pitch".format(layer),
|
2020-10-27 17:23:11 +01:00
|
|
|
design.compute_pitch(layer, False))
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-12-18 18:30:00 +01:00
|
|
|
if False:
|
2020-05-07 21:35:21 +02:00
|
|
|
from tech import preferred_directions
|
|
|
|
|
print(preferred_directions)
|
|
|
|
|
from tech import layer, layer_indices
|
|
|
|
|
for name in layer_indices:
|
|
|
|
|
if name == "active":
|
|
|
|
|
continue
|
|
|
|
|
try:
|
|
|
|
|
print("{0} width {1} space {2}".format(name,
|
2020-10-27 17:23:11 +01:00
|
|
|
getattr(design, "{}_width".format(name)),
|
|
|
|
|
getattr(design, "{}_space".format(name))))
|
2020-05-07 21:35:21 +02:00
|
|
|
|
2020-10-27 17:23:11 +01:00
|
|
|
print("pitch {0} nonpref {1}".format(getattr(design, "{}_pitch".format(name)),
|
|
|
|
|
getattr(design, "{}_nonpref_pitch".format(name))))
|
2020-05-07 21:35:21 +02:00
|
|
|
except AttributeError:
|
|
|
|
|
pass
|
2019-12-18 18:30:00 +01:00
|
|
|
import sys
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
2020-11-03 15:29:17 +01:00
|
|
|
@staticmethod
|
2020-10-27 17:23:11 +01:00
|
|
|
def compute_pitch(layer, preferred=True):
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-12-18 00:45:07 +01:00
|
|
|
"""
|
2020-05-07 21:35:21 +02:00
|
|
|
This is the preferred direction pitch
|
|
|
|
|
i.e. we take the minimum or maximum contact dimension
|
2019-12-18 00:45:07 +01:00
|
|
|
"""
|
2020-05-07 21:35:21 +02:00
|
|
|
# Find the layer stacks this is used in
|
|
|
|
|
from tech import layer_stacks
|
|
|
|
|
pitches = []
|
|
|
|
|
for stack in layer_stacks:
|
|
|
|
|
# Compute the pitch with both vias above and below (if they exist)
|
|
|
|
|
if stack[0] == layer:
|
2020-10-27 17:23:11 +01:00
|
|
|
pitches.append(design.compute_layer_pitch(stack, preferred))
|
2020-05-07 21:35:21 +02:00
|
|
|
if stack[2] == layer:
|
2020-10-27 17:23:11 +01:00
|
|
|
pitches.append(design.compute_layer_pitch(stack[::-1], True))
|
2020-05-07 21:35:21 +02:00
|
|
|
|
|
|
|
|
return max(pitches)
|
|
|
|
|
|
2020-10-27 17:23:11 +01:00
|
|
|
@staticmethod
|
|
|
|
|
def get_preferred_direction(layer):
|
|
|
|
|
return preferred_directions[layer]
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-27 17:23:11 +01:00
|
|
|
@staticmethod
|
|
|
|
|
def compute_layer_pitch(layer_stack, preferred):
|
2020-05-07 21:35:21 +02:00
|
|
|
|
2019-12-18 00:45:07 +01:00
|
|
|
(layer1, via, layer2) = layer_stack
|
2020-05-07 21:35:21 +02:00
|
|
|
try:
|
|
|
|
|
if layer1 == "poly" or layer1 == "active":
|
|
|
|
|
contact1 = getattr(contact, layer1 + "_contact")
|
|
|
|
|
else:
|
|
|
|
|
contact1 = getattr(contact, layer1 + "_via")
|
|
|
|
|
except AttributeError:
|
|
|
|
|
contact1 = getattr(contact, layer2 + "_via")
|
2019-12-18 00:07:01 +01:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
if preferred:
|
2020-10-27 17:23:11 +01:00
|
|
|
if preferred_directions[layer1] == "V":
|
2020-05-07 21:35:21 +02:00
|
|
|
contact_width = contact1.first_layer_width
|
|
|
|
|
else:
|
|
|
|
|
contact_width = contact1.first_layer_height
|
2020-01-30 02:45:33 +01:00
|
|
|
else:
|
2020-10-27 17:23:11 +01:00
|
|
|
if preferred_directions[layer1] == "V":
|
2020-05-07 21:35:21 +02:00
|
|
|
contact_width = contact1.first_layer_height
|
|
|
|
|
else:
|
|
|
|
|
contact_width = contact1.first_layer_width
|
2020-10-27 17:23:11 +01:00
|
|
|
layer_space = getattr(design, layer1 + "_space")
|
2018-07-10 00:43:26 +02:00
|
|
|
|
2020-05-07 21:35:21 +02:00
|
|
|
#print(layer_stack)
|
|
|
|
|
#print(contact1)
|
|
|
|
|
pitch = contact_width + layer_space
|
|
|
|
|
|
2020-11-14 00:55:55 +01:00
|
|
|
return utils.round_to_grid(pitch)
|
2019-12-17 20:03:36 +01:00
|
|
|
|
2020-10-27 17:28:21 +01:00
|
|
|
def setup_multiport_constants(self):
|
2020-11-03 15:29:17 +01:00
|
|
|
"""
|
2018-11-08 21:19:40 +01:00
|
|
|
These are contants and lists that aid multiport design.
|
|
|
|
|
Ports are always in the order RW, W, R.
|
|
|
|
|
Port indices start from 0 and increment.
|
|
|
|
|
A first RW port will have clk0, csb0, web0, addr0, data0
|
|
|
|
|
A first W port (with no RW ports) will be: clk0, csb0, addr0, data0
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
|
|
|
|
|
|
|
|
|
|
# These are the read/write port indices.
|
2020-10-27 17:28:21 +01:00
|
|
|
self.readwrite_ports = []
|
2018-11-08 21:19:40 +01:00
|
|
|
# These are the read/write and write-only port indices
|
2020-10-27 17:28:21 +01:00
|
|
|
self.write_ports = []
|
2018-11-17 00:03:12 +01:00
|
|
|
# These are the write-only port indices.
|
2020-10-27 17:28:21 +01:00
|
|
|
self.writeonly_ports = []
|
2020-08-17 23:35:39 +02:00
|
|
|
# These are the read/write and read-only port indices
|
2020-10-27 17:28:21 +01:00
|
|
|
self.read_ports = []
|
2018-11-17 00:03:12 +01:00
|
|
|
# These are the read-only port indices.
|
2020-10-27 17:28:21 +01:00
|
|
|
self.readonly_ports = []
|
2018-11-08 21:19:40 +01:00
|
|
|
# These are all the ports
|
2020-10-27 17:28:21 +01:00
|
|
|
self.all_ports = list(range(total_ports))
|
2020-08-17 23:35:39 +02:00
|
|
|
|
|
|
|
|
# The order is always fixed as RW, W, R
|
2018-09-28 09:11:39 +02:00
|
|
|
port_number = 0
|
|
|
|
|
for port in range(OPTS.num_rw_ports):
|
2020-10-27 17:28:21 +01:00
|
|
|
self.readwrite_ports.append(port_number)
|
|
|
|
|
self.write_ports.append(port_number)
|
|
|
|
|
self.read_ports.append(port_number)
|
2018-09-28 09:11:39 +02:00
|
|
|
port_number += 1
|
|
|
|
|
for port in range(OPTS.num_w_ports):
|
2020-10-27 17:28:21 +01:00
|
|
|
self.write_ports.append(port_number)
|
|
|
|
|
self.writeonly_ports.append(port_number)
|
2018-09-28 09:11:39 +02:00
|
|
|
port_number += 1
|
|
|
|
|
for port in range(OPTS.num_r_ports):
|
2020-10-27 17:28:21 +01:00
|
|
|
self.read_ports.append(port_number)
|
|
|
|
|
self.readonly_ports.append(port_number)
|
2019-10-03 01:26:02 +02:00
|
|
|
port_number += 1
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2019-03-05 04:27:53 +01:00
|
|
|
def analytical_power(self, corner, load):
|
2018-07-10 00:43:26 +02:00
|
|
|
""" Get total power of a module """
|
|
|
|
|
total_module_power = self.return_power()
|
|
|
|
|
for inst in self.insts:
|
2019-03-05 04:27:53 +01:00
|
|
|
total_module_power += inst.mod.analytical_power(corner, load)
|
2018-07-10 00:43:26 +02:00
|
|
|
return total_module_power
|
2020-11-03 15:29:17 +01:00
|
|
|
|
2020-10-27 17:23:11 +01:00
|
|
|
design.setup_drc_constants()
|
|
|
|
|
design.setup_layer_constants()
|
2020-11-03 15:29:17 +01:00
|
|
|
|