mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-08-29 01:24:39 +02:00
Merge remote-tracking branch 'openram_local/array_gen' into merge/full-array-gen-into-dev
# Conflicts: # technology/sky130/custom/sky130_col_cap_array.py
This commit is contained in:
@@ -66,6 +66,8 @@ class contact(hierarchy_design):
|
||||
self.offset = vector(0, 0)
|
||||
self.implant_type = implant_type
|
||||
self.well_type = well_type
|
||||
# Module does not have pins, but has empty pin list.
|
||||
self.pins = []
|
||||
self.create_layout()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
@@ -164,7 +164,7 @@ class instance(geometry):
|
||||
An instance of a module with a specified location, rotation,
|
||||
spice pins, and spice nets
|
||||
"""
|
||||
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
|
||||
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False):
|
||||
"""Initializes an instance to represent a module"""
|
||||
super().__init__()
|
||||
debug.check(mirror not in ["R90", "R180", "R270"],
|
||||
@@ -176,6 +176,8 @@ class instance(geometry):
|
||||
self.rotate = rotate
|
||||
self.offset = vector(offset).snap_to_grid()
|
||||
self.mirror = mirror
|
||||
self.is_bitcell = is_bitcell
|
||||
|
||||
# track if the instance's spice pin connections have been made
|
||||
self.connected = False
|
||||
|
||||
@@ -183,10 +185,11 @@ class instance(geometry):
|
||||
# change attributes in these spice objects
|
||||
self.spice_pins = copy.deepcopy(self.mod.pins)
|
||||
self.spice_nets = copy.deepcopy(self.mod.nets)
|
||||
for pin in self.spice_pins.values():
|
||||
pin.set_inst(self)
|
||||
for net in self.spice_nets.values():
|
||||
net.set_inst(self)
|
||||
if "contact" not in mod.name:
|
||||
for pin in self.spice_pins.values():
|
||||
pin.set_inst(self)
|
||||
for net in self.spice_nets.values():
|
||||
net.set_inst(self)
|
||||
|
||||
if OPTS.netlist_only:
|
||||
self.width = 0
|
||||
|
||||
+121
-100
@@ -9,6 +9,7 @@ import sys
|
||||
import os
|
||||
import re
|
||||
from math import sqrt
|
||||
from copy import deepcopy
|
||||
from openram import debug
|
||||
from openram.gdsMill import gdsMill
|
||||
from openram import tech
|
||||
@@ -460,21 +461,32 @@ class layout():
|
||||
for pin in pin_list:
|
||||
pin.rect = [pin.ll() - offset, pin.ur() - offset]
|
||||
|
||||
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
|
||||
def add_inst(self, name, mod, offset=[0, 0], mirror="R0", rotate=0, is_bitcell=False):
|
||||
""" Adds an instance of a mod to this module """
|
||||
# Contacts are not really instances, so skip them
|
||||
if "contact" not in mod.name:
|
||||
# Check that the instance name is unique
|
||||
debug.check(name not in self.inst_names, "Duplicate named instance in {0}: {1}".format(self.cell_name, name))
|
||||
|
||||
self.mods.add(mod)
|
||||
self.inst_names.add(name)
|
||||
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
|
||||
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate, is_bitcell))
|
||||
debug.info(3, "adding instance {}".format(self.insts[-1]))
|
||||
# This is commented out for runtime reasons
|
||||
# debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
|
||||
return self.insts[-1]
|
||||
|
||||
def add_existing_inst(self, inst, name):
|
||||
#new_inst = deepcopy(inst)
|
||||
new_inst = self.add_inst(name, inst.mod, offset=inst.offset, mirror=inst.mirror, rotate=inst.rotate, is_bitcell=inst.is_bitcell)
|
||||
#new_inst.mod = inst.mod
|
||||
self.mods.add(new_inst.mod)
|
||||
#if name:
|
||||
# new_inst.name = name
|
||||
self.inst_names.add(new_inst.name)
|
||||
#self.insts.append(new_inst)
|
||||
debug.info(3, "adding existing instance{}".format(self.insts[-1]))
|
||||
return self.insts[-1]
|
||||
|
||||
def get_inst(self, name):
|
||||
""" Retrieve an instance by name """
|
||||
for inst in self.insts:
|
||||
@@ -2149,6 +2161,13 @@ class layout():
|
||||
# Add the gnd ring
|
||||
self.add_ring([ll, ur])
|
||||
|
||||
def reset_coordinates(self):
|
||||
ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts]))
|
||||
|
||||
self.translate_all(ll)
|
||||
self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts])
|
||||
self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts])
|
||||
|
||||
def add_ring(self, bbox=None, width_mult=8, offset=0):
|
||||
"""
|
||||
Add a ring around the bbox
|
||||
@@ -2201,7 +2220,7 @@ class layout():
|
||||
size=(supply_vias,
|
||||
supply_vias))
|
||||
|
||||
def add_power_ring(self):
|
||||
def add_power_ring(self, h_layer="m2", v_layer="m1", top=True, bottom=True, left=True, right=True):
|
||||
"""
|
||||
Create vdd and gnd power rings around an area of the bounding box
|
||||
argument. Must have a supply_rail_width and supply_rail_pitch
|
||||
@@ -2209,118 +2228,120 @@ class layout():
|
||||
left/right/top/bottom vdd/gnd center offsets for use in other
|
||||
modules..
|
||||
"""
|
||||
|
||||
[ll, ur] = self.bbox
|
||||
|
||||
supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width
|
||||
supply_rail_spacing = self.supply_rail_pitch
|
||||
height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing
|
||||
width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
|
||||
|
||||
# LEFT vertical rails
|
||||
offset = ll + vector(-2 * self.supply_rail_pitch,
|
||||
-2 * self.supply_rail_pitch)
|
||||
left_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height)
|
||||
if left:
|
||||
offset = ll + vector(-2*self.supply_rail_pitch,
|
||||
-2*self.supply_rail_pitch)
|
||||
self.left_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer=v_layer,
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height + 2 * supply_rail_spacing)
|
||||
|
||||
offset = ll + vector(-1 * self.supply_rail_pitch,
|
||||
-1 * self.supply_rail_pitch)
|
||||
left_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height)
|
||||
offset = ll + vector(-1 * self.supply_rail_pitch,
|
||||
-1 * self.supply_rail_pitch)
|
||||
self.left_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer=v_layer,
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height)
|
||||
|
||||
# RIGHT vertical rails
|
||||
offset = vector(ur.x, ll.y) + vector(0, -2 * self.supply_rail_pitch)
|
||||
right_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height)
|
||||
if right:
|
||||
# RIGHT vertical railsteac a 460
|
||||
offset = vector(ur.x, ll.y) + vector(2 * self.supply_rail_pitch - self.supply_rail_width,
|
||||
-2 * self.supply_rail_pitch)
|
||||
self.right_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer=v_layer,
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height + 2* supply_rail_spacing)
|
||||
|
||||
offset = vector(ur.x, ll.y) + vector(self.supply_rail_pitch,
|
||||
-1 * self.supply_rail_pitch)
|
||||
right_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer="m2",
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height)
|
||||
offset = vector(ur.x, ll.y) + vector(1 * self.supply_rail_pitch - self.supply_rail_width,
|
||||
-1 * self.supply_rail_pitch)
|
||||
self.right_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer=v_layer,
|
||||
offset=offset,
|
||||
width=self.supply_rail_width,
|
||||
height=height)
|
||||
|
||||
# BOTTOM horizontal rails
|
||||
offset = ll + vector(-2 * self.supply_rail_pitch,
|
||||
-2 * self.supply_rail_pitch)
|
||||
bottom_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer="m1",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.supply_rail_width)
|
||||
if bottom:
|
||||
# BOTTOM horizontal rails
|
||||
offset = ll + vector(-2 * self.supply_rail_pitch,
|
||||
-2 * self.supply_rail_pitch)
|
||||
self.bottom_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer=h_layer,
|
||||
offset=offset,
|
||||
width=width + 2 * supply_rail_spacing,
|
||||
height=self.supply_rail_width)
|
||||
|
||||
offset = ll + vector(-1 * self.supply_rail_pitch,
|
||||
-1 * self.supply_rail_pitch)
|
||||
bottom_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer="m1",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.supply_rail_width)
|
||||
offset = ll + vector(-1 * self.supply_rail_pitch,
|
||||
-1 * self.supply_rail_pitch)
|
||||
self.bottom_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer=h_layer,
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.supply_rail_width)
|
||||
if top:
|
||||
# TOP horizontal rails
|
||||
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
|
||||
2 * self.supply_rail_pitch - self.supply_rail_width)
|
||||
self.top_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer=h_layer,
|
||||
offset=offset,
|
||||
width=width + 2 * supply_rail_spacing,
|
||||
height=self.supply_rail_width)
|
||||
|
||||
# TOP horizontal rails
|
||||
offset = vector(ll.x, ur.y) + vector(-2 * self.supply_rail_pitch,
|
||||
0)
|
||||
top_gnd_pin = self.add_layout_pin(text="gnd",
|
||||
layer="m1",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.supply_rail_width)
|
||||
|
||||
offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch,
|
||||
self.supply_rail_pitch)
|
||||
top_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer="m1",
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.supply_rail_width)
|
||||
offset = vector(ll.x, ur.y) + vector(-1 * self.supply_rail_pitch,
|
||||
1 * self.supply_rail_pitch - self.supply_rail_width)
|
||||
self.top_vdd_pin = self.add_layout_pin(text="vdd",
|
||||
layer=h_layer,
|
||||
offset=offset,
|
||||
width=width,
|
||||
height=self.supply_rail_width)
|
||||
|
||||
# Remember these for connecting things in the design
|
||||
self.left_gnd_x_center = left_gnd_pin.cx()
|
||||
self.left_vdd_x_center = left_vdd_pin.cx()
|
||||
self.right_gnd_x_center = right_gnd_pin.cx()
|
||||
self.right_vdd_x_center = right_vdd_pin.cx()
|
||||
|
||||
self.bottom_gnd_y_center = bottom_gnd_pin.cy()
|
||||
self.bottom_vdd_y_center = bottom_vdd_pin.cy()
|
||||
self.top_gnd_y_center = top_gnd_pin.cy()
|
||||
self.top_vdd_y_center = top_vdd_pin.cy()
|
||||
|
||||
# Find the number of vias for this pitch
|
||||
self.supply_vias = 1
|
||||
while True:
|
||||
c = factory.create(module_type="contact",
|
||||
layer_stack=self.m1_stack,
|
||||
dimensions=(self.supply_vias, self.supply_vias))
|
||||
if c.second_layer_width < self.supply_rail_width and c.second_layer_height < self.supply_rail_width:
|
||||
self.supply_vias += 1
|
||||
else:
|
||||
self.supply_vias -= 1
|
||||
break
|
||||
|
||||
via_points = [vector(self.left_gnd_x_center, self.bottom_gnd_y_center),
|
||||
vector(self.left_gnd_x_center, self.top_gnd_y_center),
|
||||
vector(self.right_gnd_x_center, self.bottom_gnd_y_center),
|
||||
vector(self.right_gnd_x_center, self.top_gnd_y_center),
|
||||
vector(self.left_vdd_x_center, self.bottom_vdd_y_center),
|
||||
vector(self.left_vdd_x_center, self.top_vdd_y_center),
|
||||
vector(self.right_vdd_x_center, self.bottom_vdd_y_center),
|
||||
vector(self.right_vdd_x_center, self.top_vdd_y_center)]
|
||||
if left:
|
||||
self.left_gnd_x_center = self.left_gnd_pin.cx()
|
||||
self.left_vdd_x_center = self.left_vdd_pin.cx()
|
||||
if right:
|
||||
self.right_gnd_x_center = self.right_gnd_pin.cx()
|
||||
self.right_vdd_x_center = self.right_vdd_pin.cx()
|
||||
if bottom:
|
||||
self.bottom_gnd_y_center = self.bottom_gnd_pin.cy()
|
||||
self.bottom_vdd_y_center = self.bottom_vdd_pin.cy()
|
||||
if top:
|
||||
self.top_gnd_y_center = self.top_gnd_pin.cy()
|
||||
self.top_vdd_y_center = self.top_vdd_pin.cy()
|
||||
|
||||
via_points = []
|
||||
if left and bottom:
|
||||
via_points.append((self.left_gnd_x_center, self.bottom_gnd_y_center))
|
||||
if left and top:
|
||||
via_points.append(vector(self.left_gnd_x_center, self.top_gnd_y_center))
|
||||
if right and bottom:
|
||||
via_points.append(vector(self.right_gnd_x_center, self.bottom_gnd_y_center))
|
||||
if right and top:
|
||||
via_points.append(vector(self.right_gnd_x_center, self.top_gnd_y_center))
|
||||
if left and bottom:
|
||||
via_points.append(vector(self.left_vdd_x_center, self.bottom_vdd_y_center))
|
||||
if left and top:
|
||||
via_points.append(vector(self.left_vdd_x_center, self.top_vdd_y_center))
|
||||
if right and bottom:
|
||||
via_points.append(vector(self.right_vdd_x_center, self.bottom_vdd_y_center))
|
||||
if right and top:
|
||||
via_points.append((self.right_vdd_x_center, self.top_vdd_y_center))
|
||||
|
||||
for pt in via_points:
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=pt,
|
||||
size=(self.supply_vias,
|
||||
self.supply_vias))
|
||||
|
||||
self.add_via_stack_center(offset=pt,
|
||||
from_layer=h_layer,
|
||||
to_layer=v_layer,
|
||||
min_area=True)
|
||||
def pdf_write(self, pdf_name):
|
||||
"""
|
||||
Display the layout to a PDF file.
|
||||
|
||||
@@ -50,6 +50,7 @@ class spice():
|
||||
if not os.path.exists(self.lvs_file):
|
||||
self.lvs_file = self.sp_file
|
||||
|
||||
self.valid_signal_types = ["INOUT", "INPUT", "OUTPUT", "BIAS", "POWER", "GROUND"]
|
||||
# Holds subckts/mods for this module
|
||||
self.mods = set()
|
||||
# Holds the pins for this module (in order)
|
||||
@@ -699,7 +700,8 @@ class spice():
|
||||
def get_instance_connections(self):
|
||||
conns = []
|
||||
for inst in self.insts:
|
||||
conns.append(inst.get_connections())
|
||||
if "contact" not in inst.name:
|
||||
conns.append(inst.get_connections())
|
||||
return conns
|
||||
|
||||
def is_net_alias(self, known_net, net_alias, mod, exclusion_set):
|
||||
|
||||
@@ -619,10 +619,10 @@ class simulation():
|
||||
Gets the signal name associated with the bitlines in the bank.
|
||||
"""
|
||||
# FIXME: change to a solution that does not depend on the technology
|
||||
if OPTS.tech_name == "sky130" and len(self.all_ports) == 1:
|
||||
cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1")
|
||||
else:
|
||||
cell_mod = factory.create(module_type=OPTS.bitcell)
|
||||
#if OPTS.tech_name == "sky130" and len(self.all_ports) == 1:
|
||||
# cell_mod = factory.create(module_type=OPTS.bitcell, version="opt1")
|
||||
#else:
|
||||
cell_mod = factory.create(module_type=OPTS.bitcell)
|
||||
cell_bl = cell_mod.get_bl_name(port)
|
||||
cell_br = cell_mod.get_br_name(port)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ simulations as well.
|
||||
"""
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import subprocess
|
||||
import numpy as np
|
||||
from openram import debug
|
||||
@@ -406,11 +407,15 @@ class stimuli():
|
||||
spice_stdout = open("{0}spice_stdout.log".format(OPTS.openram_temp), 'w')
|
||||
spice_stderr = open("{0}spice_stderr.log".format(OPTS.openram_temp), 'w')
|
||||
|
||||
# Wrap the command with conda activate & conda deactivate
|
||||
# Run spice in the Nix devShell when Nix-managed tools are enabled.
|
||||
# FIXME: Should use verify/run_script.py here but run_script doesn't return
|
||||
# the return code of the subprocess. File names might also mismatch.
|
||||
from openram import CONDA_HOME
|
||||
cmd = "/bin/bash -c 'source {0}/bin/activate && {1} && conda deactivate'".format(CONDA_HOME, cmd)
|
||||
if OPTS.use_nix:
|
||||
cmd = (
|
||||
"nix --extra-experimental-features 'nix-command flakes' "
|
||||
"develop --command /bin/bash -lc {0}"
|
||||
.format(shlex.quote(cmd))
|
||||
)
|
||||
debug.info(2, cmd)
|
||||
proc = subprocess.run(cmd, stdout=spice_stdout, stderr=spice_stderr, shell=True)
|
||||
|
||||
|
||||
@@ -144,11 +144,11 @@ class _pgate:
|
||||
|
||||
|
||||
class bitcell(cell):
|
||||
def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False):
|
||||
def __init__(self, port_order, port_types, port_map=None, storage_nets=["Q", "Q_bar"], mirror=None, end_caps=False, has_corners=True):
|
||||
super().__init__(port_order, port_types, port_map)
|
||||
|
||||
self.end_caps = end_caps
|
||||
|
||||
self.has_corners = has_corners
|
||||
if not mirror:
|
||||
self.mirror = _mirror_axis(True, False)
|
||||
else:
|
||||
|
||||
+36
-17
@@ -67,11 +67,14 @@ def parse_args():
|
||||
dest="num_sim_threads"),
|
||||
optparse.make_option("-v", "--verbose",
|
||||
action="count",
|
||||
dest="verbose_level",
|
||||
dest="5",
|
||||
help="Increase the verbosity level"),
|
||||
optparse.make_option("-t", "--tech",
|
||||
dest="tech_name",
|
||||
help="Technology name"),
|
||||
optparse.make_option("-f", "--tech_file",
|
||||
dest="tech_file",
|
||||
help="Technology name"),
|
||||
optparse.make_option("-s", "--spice",
|
||||
dest="spice_name",
|
||||
help="Spice simulator executable name"),
|
||||
@@ -185,7 +188,7 @@ def init_openram(config_file, is_unit_test=False):
|
||||
|
||||
read_config(config_file, is_unit_test)
|
||||
|
||||
install_conda()
|
||||
install_nix()
|
||||
|
||||
import_tech()
|
||||
|
||||
@@ -206,17 +209,40 @@ def init_openram(config_file, is_unit_test=False):
|
||||
from openram import verify
|
||||
|
||||
|
||||
def install_conda():
|
||||
""" Setup conda for default tools. """
|
||||
def install_nix():
|
||||
"""Initialize Nix-based toolchain dependencies."""
|
||||
|
||||
# Don't setup conda if not used
|
||||
if not OPTS.use_conda or OPTS.is_unit_test:
|
||||
# Don't setup tools during unit tests.
|
||||
if OPTS.is_unit_test:
|
||||
return
|
||||
|
||||
debug.info(1, "Creating conda setup...");
|
||||
if not OPTS.use_nix or OPTS.is_unit_test:
|
||||
return
|
||||
|
||||
from openram import CONDA_INSTALLER
|
||||
subprocess.call(CONDA_INSTALLER)
|
||||
debug.info(1, "Bootstrapping toolchain with Nix...")
|
||||
|
||||
nix_exe = shutil.which("nix")
|
||||
if nix_exe is None:
|
||||
debug.error("Nix is required for automatic tool setup, but 'nix' was not found in PATH.", -1)
|
||||
|
||||
repo_root = os.path.abspath(os.path.join(OPENRAM_HOME, ".."))
|
||||
flake_file = os.path.join(repo_root, "flake.nix")
|
||||
if not os.path.exists(flake_file):
|
||||
debug.error("Expected Nix flake at {} for tool setup.".format(flake_file), -1)
|
||||
|
||||
# Trigger materialization/build of the devShell dependencies once.
|
||||
# Environment activation still happens outside OpenRAM via `nix develop`.
|
||||
cmd = [
|
||||
nix_exe,
|
||||
"--extra-experimental-features", "nix-command flakes",
|
||||
"develop",
|
||||
"--command", "true",
|
||||
]
|
||||
result = subprocess.call(cmd, cwd=repo_root)
|
||||
if result != 0:
|
||||
debug.error("Failed to initialize Nix toolchain (nix develop returned {}).".format(result), -1)
|
||||
|
||||
return
|
||||
|
||||
|
||||
def setup_bitcell():
|
||||
@@ -443,14 +469,7 @@ def find_exe(check_exe):
|
||||
Check if the binary exists in any path dir and return the full path.
|
||||
"""
|
||||
|
||||
# Search for conda setup if used
|
||||
if OPTS.use_conda:
|
||||
from openram import CONDA_HOME
|
||||
search_path = "{0}/bin{1}{2}".format(CONDA_HOME,
|
||||
os.pathsep,
|
||||
os.environ["PATH"])
|
||||
else:
|
||||
search_path = os.environ["PATH"]
|
||||
search_path = os.environ["PATH"]
|
||||
|
||||
# Check if the preferred spice option exists in the path
|
||||
for path in search_path.split(os.pathsep):
|
||||
|
||||
@@ -41,7 +41,7 @@ from .local_bitcell_array import *
|
||||
from .nand2_dec import *
|
||||
from .nand3_dec import *
|
||||
from .nand4_dec import *
|
||||
from .orig_bitcell_array import *
|
||||
#from .orig_bitcell_array import *
|
||||
from .pand2 import *
|
||||
from .pand3 import *
|
||||
from .pand4 import *
|
||||
|
||||
@@ -10,15 +10,17 @@ from openram.tech import drc, spice
|
||||
from openram.sram_factory import factory
|
||||
from openram import OPTS
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
|
||||
from .pattern import pattern
|
||||
from openram.base import geometry, instance
|
||||
from math import ceil
|
||||
|
||||
class bitcell_array(bitcell_base_array):
|
||||
"""
|
||||
Creates a rows x cols array of memory cells.
|
||||
Assumes bit-lines and word lines are connected by abutment.
|
||||
"""
|
||||
def __init__(self, rows, cols, column_offset=0, name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
def __init__(self, rows, cols, column_offset=0, row_offset=0, name="", left_rbl=None, right_rbl=None):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||
|
||||
@@ -42,7 +44,7 @@ class bitcell_array(bitcell_base_array):
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place_array("bit_r{0}_c{1}")
|
||||
self.place_array()
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
@@ -57,18 +59,29 @@ class bitcell_array(bitcell_base_array):
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.cell)
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
r = self.row_offset
|
||||
c = self.column_offset
|
||||
self.cell_inst={}
|
||||
if self.cell.mirror.y:
|
||||
core_block = [[0 for x in range(2)] for y in range(2)]
|
||||
core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='')
|
||||
core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='MY')
|
||||
core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.cell, is_bitcell=True, mirror='MX')
|
||||
core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.cell, is_bitcell=True, mirror='XY')
|
||||
else:
|
||||
core_block = [[0 for x in range(1)] for y in range(2)]
|
||||
core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.cell, is_bitcell=True)
|
||||
core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.cell, is_bitcell=True, mirror='MX')
|
||||
#print(r, c)
|
||||
#print(core_block)
|
||||
|
||||
# If it is a "core" cell, it could be trimmed for sim time
|
||||
if col>0 and col<self.column_size-1 and row>0 and row<self.row_size-1:
|
||||
self.trim_insts.add(name)
|
||||
self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.row_size, num_cols=self.column_size,name_template="bit_r{0}_c{1}")
|
||||
self.pattern.connect_array()
|
||||
|
||||
for key in self.cell_inst.keys():
|
||||
(row, col) = key
|
||||
if col>0 and col<self.column_size-1 and row>0 and row<self.row_size-1:
|
||||
self.trim_insts.add(self.cell_inst[key].name)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
|
||||
@@ -25,6 +25,7 @@ class bitcell_base(design):
|
||||
self.nets_match = self.do_nets_exist(prop.storage_nets)
|
||||
self.mirror = prop.mirror
|
||||
self.end_caps = prop.end_caps
|
||||
self.has_corners = prop.has_corners
|
||||
def get_stage_effort(self, load):
|
||||
parasitic_delay = 1
|
||||
# This accounts for bitline being drained
|
||||
|
||||
@@ -6,23 +6,24 @@
|
||||
# All rights reserved.
|
||||
#
|
||||
from openram import debug
|
||||
from openram.base.geometry import instance
|
||||
from openram.base import design
|
||||
from openram.sram_factory import factory
|
||||
from openram import OPTS
|
||||
|
||||
from openram.modules import pattern
|
||||
|
||||
class bitcell_base_array(design):
|
||||
"""
|
||||
Abstract base class for bitcell-arrays -- bitcell, dummy, replica
|
||||
"""
|
||||
def __init__(self, name, rows, cols, column_offset):
|
||||
def __init__(self, name, rows, cols, column_offset, row_offset):
|
||||
super().__init__(name)
|
||||
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
|
||||
|
||||
self.column_size = cols
|
||||
self.row_size = rows
|
||||
self.column_offset = column_offset
|
||||
|
||||
self.row_offset = row_offset
|
||||
# Bitcell for port names only
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
@@ -120,13 +121,16 @@ class bitcell_base_array(design):
|
||||
def get_all_wordline_names(self, port=None):
|
||||
""" Return all the wordline names """
|
||||
temp = []
|
||||
temp.extend(self.get_rbl_wordline_names(0))
|
||||
if len(self.all_ports) > 0:
|
||||
temp.extend(self.get_rbl_wordline_names(0))
|
||||
if port == None:
|
||||
temp.extend(self.all_wordline_names)
|
||||
else:
|
||||
temp.extend(self.wordline_names[port])
|
||||
if len(self.all_ports) > 1:
|
||||
temp.extend(self.get_rbl_wordline_names(1))
|
||||
|
||||
|
||||
return temp
|
||||
|
||||
def add_bitline_pins(self):
|
||||
@@ -157,14 +161,54 @@ class bitcell_base_array(design):
|
||||
height=wl_pin.height())
|
||||
|
||||
def route_supplies(self):
|
||||
for inst in self.cell_inst.values():
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
vdd_found = False
|
||||
gnd_found = False
|
||||
|
||||
for inst in self.insts:
|
||||
if 'vdd' in inst.mod.get_pin_names():
|
||||
vdd_found = True
|
||||
if 'gnd' in inst.mod.get_pin_names():
|
||||
gnd_found = True
|
||||
|
||||
power_name = 'vdd'
|
||||
ground_name = 'gnd'
|
||||
|
||||
if vdd_found == False or gnd_found == False:
|
||||
for inst in self.insts:
|
||||
if 'VDD' in inst.mod.get_pin_names():
|
||||
vdd_found = True
|
||||
power_name = 'VDD'
|
||||
|
||||
if 'GND' in inst.mod.get_pin_names():
|
||||
gnd_found = True
|
||||
ground_name = 'GND'
|
||||
|
||||
|
||||
if vdd_found is False or gnd_found is False:
|
||||
from openram.tech import cell_properties
|
||||
try:
|
||||
power_name = cell_properties.power_name
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
ground_name = cell_properties.ground_name
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
for inst in self.insts:
|
||||
if power_name in inst.mod.get_pin_names():
|
||||
self.copy_layout_pin(inst, power_name, new_name='vdd')
|
||||
if ground_name in inst.mod.get_pin_names():
|
||||
self.copy_layout_pin(inst, ground_name , new_name='gnd')
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
self.add_bitline_pins()
|
||||
self.add_wl_pins()
|
||||
if self.get_bitline_names():
|
||||
self.add_bitline_pins()
|
||||
if self.get_wordline_names():
|
||||
self.add_wl_pins()
|
||||
|
||||
def _adjust_x_offset(self, xoffset, col, col_offset):
|
||||
tempx = xoffset
|
||||
@@ -184,32 +228,8 @@ class bitcell_base_array(design):
|
||||
dir_x = True
|
||||
return (tempy, dir_x)
|
||||
|
||||
def place_array(self, name_template, row_offset=0):
|
||||
# We increase it by a well enclosure so the precharges don't overlap our wells
|
||||
self.height = self.row_size * self.cell.height
|
||||
self.width = self.column_size * self.cell.width
|
||||
|
||||
xoffset = 0.0
|
||||
for col in range(self.column_size):
|
||||
yoffset = 0.0
|
||||
tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset)
|
||||
|
||||
for row in range(self.row_size):
|
||||
tempy, dir_x = self._adjust_y_offset(yoffset, row, row_offset)
|
||||
|
||||
if dir_x and dir_y:
|
||||
dir_key = "XY"
|
||||
elif dir_x:
|
||||
dir_key = "MX"
|
||||
elif dir_y:
|
||||
dir_key = "MY"
|
||||
else:
|
||||
dir_key = ""
|
||||
|
||||
self.cell_inst[row, col].place(offset=[tempx, tempy],
|
||||
mirror=dir_key)
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
def place_array(self):
|
||||
self.pattern.place_array()
|
||||
|
||||
def get_column_offsets(self):
|
||||
"""
|
||||
|
||||
@@ -20,7 +20,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
sides of a bitcell array.
|
||||
"""
|
||||
def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""):
|
||||
super().__init__(name, rows, cols, column_offset=0)
|
||||
super().__init__(name, rows, cols, column_offset=0, row_offset=0)
|
||||
debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name,
|
||||
rows,
|
||||
cols,
|
||||
@@ -51,10 +51,10 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
self.rbls = self.left_rbl + self.right_rbl
|
||||
|
||||
# Two dummy rows plus replica even if we don't add the column
|
||||
self.extra_rows = sum(self.rbl)
|
||||
self.extra_rows = sum(self.rbl) + 2
|
||||
# If we aren't using row/col caps, then we need to use the bitcell
|
||||
if not self.cell.end_caps:
|
||||
self.extra_rows += 2
|
||||
#if not self.cell.end_caps:
|
||||
# self.extra_rows += 2
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
@@ -73,6 +73,8 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
cols=self.column_size,
|
||||
rows=self.row_size,
|
||||
rbl=self.rbl,
|
||||
column_offset=1,
|
||||
row_offset=1,
|
||||
left_rbl=self.left_rbl,
|
||||
right_rbl=self.right_rbl)
|
||||
|
||||
@@ -86,35 +88,39 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
rows=1,
|
||||
# dummy column + left replica column(s)
|
||||
column_offset=1,
|
||||
row_offset=self.row_size+ self.extra_rows + 1, #add 1 to account for bottom col_cap
|
||||
mirror=0,
|
||||
location="top")
|
||||
location="top",
|
||||
left_rbl=self.left_rbl,
|
||||
right_rbl=self.right_rbl)
|
||||
|
||||
self.col_cap_bottom = factory.create(module_type=col_cap_module_type,
|
||||
cols=self.column_size + len(self.rbls),
|
||||
rows=1,
|
||||
# dummy column + left replica column(s)
|
||||
column_offset=1,
|
||||
mirror=0,
|
||||
location="bottom")
|
||||
row_offset=0,
|
||||
mirror=(1+self.row_size+self.extra_rows) % 2,
|
||||
location="bottom",
|
||||
left_rbl=self.left_rbl,
|
||||
right_rbl=self.right_rbl)
|
||||
|
||||
# Dummy Col or Row Cap, depending on bitcell array properties
|
||||
row_cap_module_type = ("row_cap_array" if self.cell.end_caps else "dummy_array")
|
||||
|
||||
self.row_cap_left = factory.create(module_type=row_cap_module_type,
|
||||
cols=1,
|
||||
column_offset=0,
|
||||
rows=self.row_size + self.extra_rows,
|
||||
mirror=(self.rbl[0] + 1) % 2)
|
||||
column_offset=0,
|
||||
row_offset=0,
|
||||
location="left")
|
||||
|
||||
self.row_cap_right = factory.create(module_type=row_cap_module_type,
|
||||
cols=1,
|
||||
# dummy column
|
||||
# + left replica column(s)
|
||||
# + bitcell columns
|
||||
# + right replica column(s)
|
||||
column_offset=1 + len(self.left_rbl) + self.column_size + self.rbl[0],
|
||||
rows=self.row_size + self.extra_rows,
|
||||
mirror=(self.rbl[0] + 1) % 2)
|
||||
column_offset=1 + len(self.left_rbl) + self.column_size + len(self.right_rbl),
|
||||
row_offset=0,
|
||||
location="right")
|
||||
|
||||
def add_pins(self):
|
||||
|
||||
@@ -158,10 +164,21 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
self.unused_wordline_names = self.replica_bitcell_array.unused_wordline_names
|
||||
self.replica_array_wordline_names_with_grounded_wls = ["gnd" if x in self.unused_wordline_names else x for x in self.replica_bitcell_array.wordline_pin_list]
|
||||
|
||||
# Left/right row caps cover the full array height. Pad with gnd so the
|
||||
# netlist list length matches the row cap (replica in the center); do
|
||||
# not use col cap wordline heuristics.
|
||||
|
||||
n_rowcap_wl = len(self.row_cap_left.get_wordline_names())
|
||||
n_rba_wl = len(self.replica_array_wordline_names_with_grounded_wls)
|
||||
|
||||
|
||||
self.wordline_pin_list = []
|
||||
self.wordline_pin_list.extend(["gnd"] * len(self.col_cap_top.get_wordline_names()))
|
||||
|
||||
if self.rbls:
|
||||
self.wordline_pin_list.extend(["gnd"] * len(self.rbls))
|
||||
self.wordline_pin_list.extend(self.replica_array_wordline_names_with_grounded_wls)
|
||||
self.wordline_pin_list.extend(["gnd"] * len(self.col_cap_bottom.get_wordline_names()))
|
||||
if self.rbls:
|
||||
self.wordline_pin_list.extend(["gnd"] * len(self.rbls))
|
||||
|
||||
self.add_pin_list(self.used_wordline_names, "INPUT")
|
||||
|
||||
@@ -177,7 +194,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
# Top/bottom dummy rows or col caps
|
||||
self.dummy_row_insts = []
|
||||
self.dummy_row_insts.append(self.add_inst(name="dummy_row_bot",
|
||||
mod=self.col_cap_bottom))
|
||||
mod=self.col_cap_bottom,))
|
||||
self.connect_inst(self.bitline_pin_list + ["gnd"] * len(self.col_cap_bottom.get_wordline_names()) + self.supplies)
|
||||
self.dummy_row_insts.append(self.add_inst(name="dummy_row_top",
|
||||
mod=self.col_cap_top))
|
||||
@@ -188,6 +205,10 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
self.dummy_col_insts.append(self.add_inst(name="dummy_col_left",
|
||||
mod=self.row_cap_left))
|
||||
self.connect_inst(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.wordline_pin_list + self.supplies)
|
||||
|
||||
#print(self.dummy_col_insts[0].mod.pins)
|
||||
#print(["dummy_left_" + bl for bl in self.row_cap_left.all_bitline_names] + self.wordline_pin_list + self.supplies)
|
||||
|
||||
self.dummy_col_insts.append(self.add_inst(name="dummy_col_right",
|
||||
mod=self.row_cap_right))
|
||||
self.connect_inst(["dummy_right_" + bl for bl in self.row_cap_right.all_bitline_names] + self.wordline_pin_list + self.supplies)
|
||||
@@ -201,40 +222,34 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
# row-based or column based power and ground lines.
|
||||
self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0]))
|
||||
self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2]))
|
||||
# FIXME: custom sky130 replica module has a better version of this offset
|
||||
self.unused_offset = vector(0.25, 0.25)
|
||||
|
||||
# This is a bitcell x bitcell offset to scale
|
||||
self.bitcell_offset = vector(self.cell.width, self.cell.height)
|
||||
self.col_end_offset = vector(self.cell.width, self.cell.height)
|
||||
self.row_end_offset = vector(self.cell.width, self.cell.height)
|
||||
|
||||
# Everything is computed with the replica array
|
||||
self.replica_bitcell_array_inst.place(offset=self.unused_offset)
|
||||
self.replica_bitcell_array_inst.place(offset=0)
|
||||
|
||||
self.add_end_caps()
|
||||
|
||||
# shift everything up and right to account for cap cells
|
||||
self.translate_all(self.bitcell_offset.scale(-1, -1))
|
||||
|
||||
self.width = self.dummy_col_insts[1].rx() + self.unused_offset.x
|
||||
self.height = self.dummy_row_insts[1].uy()
|
||||
|
||||
self.add_layout_pins()
|
||||
ll = vector(-1 * self.dummy_col_insts[0].width, -1 * self.dummy_row_insts[0].height)
|
||||
self.translate_all(ll)
|
||||
self.capped_rba_width = self.dummy_col_insts[0].width + self.dummy_row_insts[0].width + self.dummy_col_insts[1].width
|
||||
self.capped_rba_height = self.dummy_col_insts[0].height
|
||||
|
||||
|
||||
self.route_power_ring(self.supply_stack[2], self.supply_stack[0])
|
||||
self.route_supplies()
|
||||
|
||||
self.route_unused_wordlines()
|
||||
|
||||
lower_left = self.find_lowest_coords()
|
||||
upper_right = self.find_highest_coords()
|
||||
self.width = upper_right.x - lower_left.x
|
||||
self.height = upper_right.y - lower_left.y
|
||||
self.translate_all(lower_left)
|
||||
|
||||
self.reset_coordinates()
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def route_power_ring(self, v_layer, h_layer):
|
||||
self.bbox = (vector(0,0), vector(self.capped_rba_width, self.capped_rba_height))
|
||||
self.supply_rail_width = drc["minwidth_m3"]
|
||||
self.supply_rail_pitch = 6 * self.supply_rail_width
|
||||
self.add_power_ring(v_layer=v_layer, h_layer=h_layer)
|
||||
|
||||
def get_main_array_top(self):
|
||||
return self.replica_bitcell_array_inst.by() + self.replica_bitcell_array.get_main_array_top()
|
||||
@@ -248,7 +263,7 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
def get_main_array_right(self):
|
||||
return self.replica_bitcell_array_inst.lx() + self.replica_bitcell_array.get_main_array_right()
|
||||
|
||||
# FIXME: these names need to be changed to reflect what they're actually returning
|
||||
#FIXME: these names need to be changed to reflect what they're actually returning
|
||||
def get_replica_top(self):
|
||||
return self.dummy_row_insts[1].by()
|
||||
|
||||
@@ -261,7 +276,6 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
def get_replica_right(self):
|
||||
return self.dummy_col_insts[1].rx()
|
||||
|
||||
|
||||
def get_column_offsets(self):
|
||||
"""
|
||||
Return an array of the x offsets of all the regular bits
|
||||
@@ -273,47 +287,50 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
def add_end_caps(self):
|
||||
""" Add dummy cells or end caps around the array """
|
||||
|
||||
# Far top dummy row (first row above array is NOT flipped if even number of rows)
|
||||
flip_dummy = (self.row_size + self.rbl[1]) % 2
|
||||
dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy) + self.replica_bitcell_array_inst.ul()
|
||||
self.dummy_row_insts[1].place(offset=dummy_row_offset,
|
||||
mirror="MX" if flip_dummy else "R0")
|
||||
# Far top dummy row
|
||||
offset = self.replica_bitcell_array_inst.ul()
|
||||
self.dummy_row_insts[1].place(offset=offset)
|
||||
|
||||
# Far bottom dummy row (first row below array IS flipped)
|
||||
flip_dummy = (self.rbl[0] + 1) % 2
|
||||
dummy_row_offset = self.bitcell_offset.scale(0, flip_dummy - 1) + self.unused_offset
|
||||
self.dummy_row_insts[0].place(offset=dummy_row_offset,
|
||||
mirror="MX" if flip_dummy else "R0")
|
||||
# Far bottom dummy row
|
||||
dummy_row_height = vector(0, self.dummy_row_insts[0].height)
|
||||
offset = self.replica_bitcell_array_inst.ll() - dummy_row_height
|
||||
self.dummy_row_insts[0].place(offset=offset)
|
||||
|
||||
# Far left dummy col
|
||||
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
|
||||
dummy_col_offset = self.bitcell_offset.scale(-1, -1) + self.unused_offset
|
||||
self.dummy_col_insts[0].place(offset=dummy_col_offset)
|
||||
dummy_col_width = vector(self.dummy_col_insts[0].width, 0)
|
||||
offset = self.dummy_row_insts[0].ll() - dummy_col_width
|
||||
if self.dummy_col_insts[0].mod.cell.has_corners is False:
|
||||
offset += vector(0, dummy_row_height.y)
|
||||
self.dummy_col_insts[0].place(offset=offset)
|
||||
|
||||
# Far right dummy col
|
||||
# Shifted down by the number of left RBLs even if we aren't adding replica column to this bitcell array
|
||||
dummy_col_offset = self.bitcell_offset.scale(0, -1) + self.replica_bitcell_array_inst.lr()
|
||||
self.dummy_col_insts[1].place(offset=dummy_col_offset)
|
||||
offset = self.dummy_row_insts[0].lr()
|
||||
if self.dummy_col_insts[0].mod.cell.has_corners is False:
|
||||
offset += vector(0, dummy_row_height.y)
|
||||
self.dummy_col_insts[1].place(offset=offset)
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
for pin_name in self.used_wordline_names + self.bitline_pin_list:
|
||||
pin = self.replica_bitcell_array_inst.get_pin(pin_name)
|
||||
|
||||
if "wl" in pin_name:
|
||||
# wordlines
|
||||
pin_offset = pin.ll().scale(0, 1)
|
||||
pin_width = self.width
|
||||
pin_width = self.capped_rba_width
|
||||
pin_height = pin.height()
|
||||
else:
|
||||
# bitlines
|
||||
pin_offset = pin.ll().scale(1, 0)
|
||||
pin_width = pin.width()
|
||||
pin_height = self.height
|
||||
pin_height = self.capped_rba_height
|
||||
|
||||
self.add_layout_pin(text=pin_name,
|
||||
layer=pin.layer,
|
||||
offset=pin_offset,
|
||||
width=pin_width,
|
||||
height=pin_height)
|
||||
|
||||
|
||||
def route_supplies(self):
|
||||
|
||||
@@ -321,72 +338,100 @@ class capped_replica_bitcell_array(bitcell_base_array):
|
||||
bitcell = factory.create(module_type="pbitcell")
|
||||
else:
|
||||
bitcell = getattr(props, "bitcell_{}port".format(OPTS.num_ports))
|
||||
top = True
|
||||
bottom = True
|
||||
left = False
|
||||
right = False
|
||||
|
||||
if top:
|
||||
inst = self.dummy_row_insts[1]
|
||||
if 'vdd' in inst.mod.pins:
|
||||
array_pins = inst.get_pins('vdd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.top_vdd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
|
||||
|
||||
vdd_dir = bitcell.vdd_dir
|
||||
gnd_dir = bitcell.gnd_dir
|
||||
array_pins = inst.get_pins('gnd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.top_gnd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
|
||||
if bottom:
|
||||
inst = self.dummy_row_insts[0]
|
||||
if 'vdd' in inst.mod.pins:
|
||||
array_pins = inst.get_pins('vdd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.bottom_vdd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
|
||||
|
||||
# vdd/gnd are only connected in the perimeter cells
|
||||
supply_insts = self.dummy_col_insts + self.dummy_row_insts
|
||||
array_pins = inst.get_pins('gnd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.bottom_gnd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(array_pin.center()[0], supply_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(array_pin.center()[0], supply_pin.center()[1]))
|
||||
if left:
|
||||
inst = self.dummy_col_insts[0]
|
||||
if 'vnd' in inst.mod.pins:
|
||||
array_pins = inst.get_pins('vnd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.left_vdd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
|
||||
|
||||
# For the wordlines
|
||||
top_bot_mult = 1
|
||||
left_right_mult = 1
|
||||
|
||||
# There are always vertical pins for the WLs on the left/right if we have unused wordlines
|
||||
self.left_gnd_locs = self.route_side_pin("gnd", "left", left_right_mult)
|
||||
self.right_gnd_locs = self.route_side_pin("gnd", "right", left_right_mult)
|
||||
# This needs to be big enough so that they aren't in the same supply routing grid
|
||||
left_right_mult = 4
|
||||
|
||||
if gnd_dir == "V":
|
||||
self.top_gnd_locs = self.route_side_pin("gnd", "top", top_bot_mult)
|
||||
self.bot_gnd_locs = self.route_side_pin("gnd", "bot", top_bot_mult)
|
||||
# This needs to be big enough so that they aren't in the same supply routing grid
|
||||
top_bot_mult = 4
|
||||
|
||||
if vdd_dir == "V":
|
||||
self.top_vdd_locs = self.route_side_pin("vdd", "top", top_bot_mult)
|
||||
self.bot_vdd_locs = self.route_side_pin("vdd", "bot", top_bot_mult)
|
||||
elif vdd_dir == "H":
|
||||
self.left_vdd_locs = self.route_side_pin("vdd", "left", left_right_mult)
|
||||
self.right_vdd_locs = self.route_side_pin("vdd", "right", left_right_mult)
|
||||
else:
|
||||
debug.error("Invalid vdd direction {}".format(vdd_dir), -1)
|
||||
|
||||
for inst in supply_insts:
|
||||
for pin in inst.get_pins("vdd"):
|
||||
if vdd_dir == "V":
|
||||
self.connect_side_pin(pin, "top", self.top_vdd_locs[0].y)
|
||||
self.connect_side_pin(pin, "bot", self.bot_vdd_locs[0].y)
|
||||
elif vdd_dir == "H":
|
||||
self.connect_side_pin(pin, "left", self.left_vdd_locs[0].x)
|
||||
self.connect_side_pin(pin, "right", self.right_vdd_locs[0].x)
|
||||
|
||||
for inst in supply_insts:
|
||||
for pin in inst.get_pins("gnd"):
|
||||
if gnd_dir == "V":
|
||||
self.connect_side_pin(pin, "top", self.top_gnd_locs[0].y)
|
||||
self.connect_side_pin(pin, "bot", self.bot_gnd_locs[0].y)
|
||||
elif gnd_dir == "H":
|
||||
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
|
||||
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
|
||||
array_pins = inst.get_pins('gnd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.left_gnd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
|
||||
if right:
|
||||
inst = self.dummy_col_insts[1]
|
||||
if 'vdd' in inst.mod.pins:
|
||||
array_pins = inst.get_pins('vdd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.right_vdd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
|
||||
|
||||
array_pins = inst.get_pins('gnd')
|
||||
for array_pin in array_pins:
|
||||
supply_pin = self.right_gnd_pin
|
||||
self.add_path(array_pin.layer, [array_pin.center(), vector(supply_pin.center()[0], array_pin.center()[1])])
|
||||
self.add_via_stack_center(from_layer = array_pin.layer,
|
||||
to_layer = supply_pin.layer,
|
||||
offset = vector(supply_pin.center()[0], array_pin.center()[1]))
|
||||
|
||||
def route_unused_wordlines(self):
|
||||
"""
|
||||
Connect the unused RBL and dummy wordlines to gnd
|
||||
"""
|
||||
# This grounds all the dummy row word lines
|
||||
for inst in self.dummy_row_insts:
|
||||
for wl_name in self.col_cap_top.get_wordline_names():
|
||||
for wl_name in inst.mod.get_wordline_names():
|
||||
pin = inst.get_pin(wl_name)
|
||||
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
|
||||
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
|
||||
self.connect_side_pin(pin, "left", self.left_gnd_pin.cx())
|
||||
self.connect_side_pin(pin, "right", self.right_gnd_pin.cx())
|
||||
|
||||
# Ground the unused replica wordlines
|
||||
for wl_name in self.unused_wordline_names:
|
||||
pin = self.replica_bitcell_array_inst.get_pin(wl_name)
|
||||
self.connect_side_pin(pin, "left", self.left_gnd_locs[0].x)
|
||||
self.connect_side_pin(pin, "right", self.right_gnd_locs[0].x)
|
||||
self.connect_side_pin(pin, "left", self.left_gnd_pin.cx())
|
||||
self.connect_side_pin(pin, "right", self.right_gnd_pin.cx())
|
||||
|
||||
def route_side_pin(self, name, side, offset_multiple=1):
|
||||
"""
|
||||
|
||||
@@ -6,14 +6,16 @@
|
||||
from openram.sram_factory import factory
|
||||
from openram import OPTS
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
|
||||
from openram.base import geometry
|
||||
from .pattern import pattern
|
||||
from math import ceil
|
||||
|
||||
class col_cap_array(bitcell_base_array):
|
||||
"""
|
||||
Generate a dummy row/column for the replica array.
|
||||
"""
|
||||
def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name="",left_rbl=[],right_rbl=[]):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name)
|
||||
self.mirror = mirror
|
||||
self.location = location
|
||||
|
||||
@@ -37,28 +39,44 @@ class col_cap_array(bitcell_base_array):
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place_array("dummy_r{0}_c{1}", self.mirror)
|
||||
self.place_array()
|
||||
self.add_layout_pins()
|
||||
|
||||
self.height = self.dummy_cell.height
|
||||
self.width = self.column_size * self.cell.width
|
||||
#self.height = self.dummy_cell.height
|
||||
#self.width = self.column_size * self.cell.width
|
||||
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.dummy_cell = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
||||
self.colend = factory.create(module_type="col_cap_{}".format(OPTS.bitcell))
|
||||
|
||||
# def create_instances(self):
|
||||
# """ Create the module instances used in this design """
|
||||
# self.cell_inst = {}
|
||||
# for col in range(self.column_size):
|
||||
# for row in range(self.row_size):
|
||||
# name = "bit_r{0}_c{1}".format(row, col)
|
||||
# self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
# mod=self.dummy_cell)
|
||||
# self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
self.cell_inst={}
|
||||
if self.row_offset % 2 == 0:
|
||||
bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="MY")]\
|
||||
+ [geometry.instance("01_colend", mod=self.colend, is_bitcell=True)]
|
||||
else:
|
||||
bit_row = [geometry.instance("00_colend", mod=self.colend, is_bitcell=True, mirror="XY")]\
|
||||
+ [geometry.instance("01_colend", mod=self.colend, is_bitcell=True, mirror="MX")]
|
||||
|
||||
bit_row = pattern.rotate_list(bit_row, self.column_offset * 2)
|
||||
bit_block = []
|
||||
pattern.append_row_to_block(bit_block, bit_row)
|
||||
self.pattern = pattern(self, "col_cap_array_" + self.location , bit_block, num_rows=self.row_size, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="col_cap_array" + self.location + "_r{0}_c{1}")
|
||||
self.pattern.connect_array()
|
||||
|
||||
def get_bitcell_pins(self, row, col):
|
||||
"""
|
||||
@@ -79,24 +97,24 @@ class col_cap_array(bitcell_base_array):
|
||||
|
||||
return bitcell_pins
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
# def add_layout_pins(self):
|
||||
# """ Add the layout pins """
|
||||
|
||||
column_list = self.cell.get_all_bitline_names()
|
||||
# column_list = self.cell.get_all_bitline_names()
|
||||
|
||||
for col in range(self.column_size):
|
||||
for cell_column in column_list:
|
||||
bl_pin = self.cell_inst[0, col].get_pin(cell_column)
|
||||
self.add_layout_pin(text=cell_column + "_{0}".format(col),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll().scale(1, 0),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
# for col in range(self.column_size):
|
||||
# for cell_column in column_list:
|
||||
# bl_pin = self.cell_inst[0, col].get_pin(cell_column)
|
||||
# self.add_layout_pin(text=cell_column + "_{0}".format(col),
|
||||
# layer=bl_pin.layer,
|
||||
# offset=bl_pin.ll().scale(1, 0),
|
||||
# width=bl_pin.width(),
|
||||
# height=self.height)
|
||||
|
||||
# Add vdd/gnd via stacks
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row, col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for pin in inst.get_pins(pin_name):
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
# # Add vdd/gnd via stacks
|
||||
# for row in range(self.row_size):
|
||||
# for col in range(self.column_size):
|
||||
# inst = self.cell_inst[row, col]
|
||||
# for pin_name in ["vdd", "gnd"]:
|
||||
# for pin in inst.get_pins(pin_name):
|
||||
# self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
@@ -6,16 +6,20 @@
|
||||
from openram.sram_factory import factory
|
||||
from openram import OPTS
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
|
||||
from openram.base import geometry
|
||||
from .pattern import pattern
|
||||
|
||||
class dummy_array(bitcell_base_array):
|
||||
"""
|
||||
Generate a dummy row/column for the replica array.
|
||||
"""
|
||||
def __init__(self, rows, cols, column_offset=0, mirror=0, location="", name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
self.mirror = mirror
|
||||
|
||||
def __init__(self, rows, cols, column_offset=0, row_offset=0 ,mirror=0, location="", name="", left_rbl=0, right_rbl=0):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name)
|
||||
self.location = location
|
||||
self.row_offset = row_offset
|
||||
self.mirror = mirror
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
@@ -32,7 +36,7 @@ class dummy_array(bitcell_base_array):
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place_array("dummy_r{0}_c{1}", self.mirror)
|
||||
self.place_array()
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
@@ -50,13 +54,24 @@ class dummy_array(bitcell_base_array):
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
self.cell_inst={}
|
||||
r = self.row_offset
|
||||
c = self.column_offset
|
||||
if self.cell.mirror.y:
|
||||
core_block = [[0 for x in range(2)] for y in range(2)]
|
||||
core_block[(0 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='')
|
||||
core_block[(0 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(0 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MY')
|
||||
core_block[(1 + r) % 2][(0+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(0+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='MX')
|
||||
core_block[(1 + r) % 2][(1+c) %2] = geometry.instance(f"core_{(1 + r)%2}_{(1+c)%2}", mod=self.dummy_cell, is_bitcell=True, mirror='XY')
|
||||
else:
|
||||
core_block = [[0 for x in range(1)] for y in range(2)]
|
||||
core_block[(0 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_0_0", mod=self.dummy_cell, is_bitcell=True)
|
||||
core_block[(1 + self.row_offset) % 2][(0+self.column_offset) %2] = geometry.instance("core_1_0", mod=self.dummy_cell, is_bitcell=True, mirror='MX')
|
||||
#print(r, c)
|
||||
#print(core_block)
|
||||
|
||||
self.pattern = pattern(self, "dummy_array", core_block, num_rows=self.row_size, num_cols=self.column_size, name_template="bit_r{0}_c{1}")
|
||||
self.pattern.connect_array()
|
||||
|
||||
def add_pins(self):
|
||||
# bitline pins are not added because they are floating
|
||||
@@ -68,47 +83,6 @@ class dummy_array(bitcell_base_array):
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
# Add the bitline metal, but not as pins since they are going to just be floating
|
||||
# For some reason, LVS has an issue if we don't add this metal
|
||||
bitline_names = self.cell.get_all_bitline_names()
|
||||
for col in range(self.column_size):
|
||||
for port in self.all_ports:
|
||||
bl_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port])
|
||||
self.add_layout_pin(text="bl_{0}_{1}".format(port, col),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll().scale(1, 0),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
br_pin = self.cell_inst[0, col].get_pin(bitline_names[2 * port + 1])
|
||||
self.add_layout_pin(text="br_{0}_{1}".format(port, col),
|
||||
layer=br_pin.layer,
|
||||
offset=br_pin.ll().scale(1, 0),
|
||||
width=br_pin.width(),
|
||||
height=self.height)
|
||||
|
||||
wl_names = self.cell.get_all_wl_names()
|
||||
for row in range(self.row_size):
|
||||
for port in self.all_ports:
|
||||
wl_pins = self.cell_inst[row, 0].get_pins(wl_names[port])
|
||||
for wl_pin in wl_pins:
|
||||
self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
|
||||
layer=wl_pin.layer,
|
||||
offset=wl_pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
def route_supplies(self):
|
||||
|
||||
# Copy a vdd/gnd layout pin from every cell
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row, col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
def input_load(self):
|
||||
# FIXME: This appears to be old code from previous characterization. Needs to be updated.
|
||||
wl_wire = self.gen_wl_wire()
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2024 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.
|
||||
#
|
||||
from openram.sram_factory import factory
|
||||
from openram.tech import drc, spice
|
||||
from openram import OPTS
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
|
||||
|
||||
class bitcell_array(bitcell_base_array):
|
||||
"""
|
||||
Creates a rows x cols array of memory cells. Assumes bit-lines
|
||||
and word line is connected by abutment.
|
||||
Connects the word lines and bit lines.
|
||||
"""
|
||||
def __init__(self, cols, rows, name, column_offset=0):
|
||||
super().__init__(cols, rows, name, column_offset)
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
# We don't offset this because we need to align
|
||||
# the replica bitcell in the control logic
|
||||
# self.offset_all_coordinates()
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_modules()
|
||||
self.add_pins()
|
||||
self.create_instances()
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place_array("bit_r{0}_c{1}")
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.cell)
|
||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
|
||||
# Dynamic Power from Bitline
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
bl_swing = OPTS.rbl_delay_percentage
|
||||
freq = spice["default_event_frequency"]
|
||||
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
||||
|
||||
# Calculate the bitcell power which currently only includes leakage
|
||||
cell_power = self.cell.analytical_power(corner, load)
|
||||
|
||||
# Leakage power grows with entire array and bitlines.
|
||||
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
|
||||
cell_power.leakage * self.column_size * self.row_size)
|
||||
return total_power
|
||||
|
||||
def gen_wl_wire(self):
|
||||
if OPTS.netlist_only:
|
||||
width = 0
|
||||
else:
|
||||
width = self.width
|
||||
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1"))
|
||||
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
|
||||
return wl_wire
|
||||
|
||||
def gen_bl_wire(self):
|
||||
if OPTS.netlist_only:
|
||||
height = 0
|
||||
else:
|
||||
height = self.height
|
||||
bl_pos = 0
|
||||
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
|
||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||
return bl_wire
|
||||
|
||||
def get_wordline_cin(self):
|
||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||
bitcell_wl_cin = self.cell.get_wl_cin()
|
||||
total_cin = bitcell_wl_cin * self.column_size
|
||||
return total_cin
|
||||
|
||||
def graph_exclude_bits(self, targ_row, targ_col):
|
||||
"""Excludes bits in column from being added to graph except target"""
|
||||
# Function is not robust with column mux configurations
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
if row == targ_row and col == targ_col:
|
||||
continue
|
||||
self.graph_inst_exclude.add(self.cell_inst[row, col])
|
||||
|
||||
def get_cell_name(self, inst_name, row, col):
|
||||
"""Gets the spice name of the target bitcell."""
|
||||
return inst_name + "{}x".format(OPTS.hier_seperator) + self.cell_inst[row, col].name, self.cell_inst[row, col]
|
||||
@@ -0,0 +1,219 @@
|
||||
# See LICENSE for licensing information.
|
||||
#
|
||||
# Copyright (c) 2016-2023 Regents of the University of California, Santa Cruz
|
||||
# All rights reserved.
|
||||
#
|
||||
from openram import debug
|
||||
from openram.base.geometry import instance,geometry
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from openram.base import design
|
||||
from openram.globals import OPTS
|
||||
from math import ceil, floor
|
||||
from copy import deepcopy
|
||||
|
||||
class pattern():
|
||||
"""
|
||||
This class is used to desribe the internals of a bitcell array. It describes
|
||||
instance modules, rotation, and ordering
|
||||
|
||||
"""
|
||||
block = List[List[instance]]
|
||||
def __init__(self,
|
||||
parent_design: design,
|
||||
name:str,
|
||||
core_block:block,
|
||||
num_rows:int,
|
||||
num_cols:int,
|
||||
name_template,
|
||||
num_cores_x: Optional[int] = 0,
|
||||
num_cores_y: Optional[int] = 0,
|
||||
cores_per_x_block: int = 1,
|
||||
cores_per_y_block: int = 1,
|
||||
x_block: Optional[block] = None,
|
||||
y_block: Optional[block] = None,
|
||||
xy_block: Optional[block] = None,
|
||||
initial_x_block:bool = False,
|
||||
initial_y_block:bool = False,
|
||||
final_x_block:bool = False,
|
||||
final_y_block:bool = False,
|
||||
):
|
||||
"""
|
||||
a "block" is a 2d list of instances
|
||||
core_block defines the main block that is tiled
|
||||
num_core defines the number of times the core block is to be tiled
|
||||
(i.e. bitcells in dimension / bitcells in core_block)
|
||||
x_block defines a block that is inserted to the right of the core_block
|
||||
y_block defines a block that is inserted below the core block
|
||||
xy_block defines a block that is a inserted where the x_block and y_block intercept
|
||||
the initial and final booleans determine whether the pattern begins and/or ends with x/y blocks
|
||||
"""
|
||||
self.parent_design = parent_design
|
||||
self.name = name
|
||||
self.core_block = core_block
|
||||
self.num_rows = num_rows
|
||||
self.num_cols = num_cols
|
||||
self.name_template = name_template
|
||||
self.num_cores_x = num_cores_x
|
||||
self.num_cores_y = num_cores_y
|
||||
if num_cores_x == 0:
|
||||
self.num_cores_x = ceil(num_cols/len(core_block[0]))
|
||||
if num_cores_y == 0:
|
||||
self.num_cores_y = ceil(num_rows/len(core_block))
|
||||
|
||||
self.cores_per_x_block = cores_per_x_block
|
||||
self.cores_per_y_block = cores_per_y_block
|
||||
self.x_block = x_block
|
||||
self.y_block = y_block
|
||||
self.xy_block = xy_block
|
||||
self.initial_x_block = initial_x_block
|
||||
self.initial_y_block = initial_y_block
|
||||
self.final_x_block = final_x_block
|
||||
self.final_y_block = final_y_block
|
||||
self.bits_per_row = ceil(self.num_rows/self.num_cores_x)
|
||||
self.bits_per_col = ceil(self.num_cols/self.num_cores_y)
|
||||
self.bit_rows = []
|
||||
self.bit_cols = []
|
||||
self.parent_design.all_inst = {}
|
||||
if not OPTS.netlist_only:
|
||||
self.verify_interblock_dimensions()
|
||||
|
||||
def compute_and_verify_intrablock_dimensions(self, block: block) -> List[int]:
|
||||
for row in block:
|
||||
row_height = row[0].height
|
||||
for inst in row:
|
||||
debug.check(row_height == inst.height, "intrablock instances within the same row are different heights")
|
||||
|
||||
for y in range(len(block[0])):
|
||||
debug.check(all([row[y].width for row in block]), "intrablock instances within the same column are different widths")
|
||||
|
||||
block_width = sum([instance.width for instance in block[0]])
|
||||
block_height = sum([row[0].height for row in block])
|
||||
|
||||
return [block_width, block_height]
|
||||
|
||||
def verify_interblock_dimensions(self) -> None:
|
||||
"""
|
||||
Ensure the individual blocks are valid and interblock dimensions are valid
|
||||
"""
|
||||
debug.check(len(self.core_block) >= 1, "invalid core_block dimension: core_block rows must be >=1")
|
||||
debug.check(len(self.core_block[0]) >= 1, "invalid core_block dimension: core_block cols must be >=1")
|
||||
if self.x_block and self.y_block:
|
||||
debug.check(self.xy_block is not None, "must have xy_block if both x_block and y_block are provided")
|
||||
|
||||
|
||||
(self.core_block_width, self.core_block_height) = self.compute_and_verify_intrablock_dimensions(self.core_block)
|
||||
if self.x_block:
|
||||
(self.x_block_width, self.x_block_height) = self.compute_and_verify_intrablock_dimensions(self.x_block)
|
||||
if self.y_block:
|
||||
(self.y_block_width, self.y_block_height) = self.compute_and_verify_intrablock_dimensions(self.y_block)
|
||||
if self.xy_block:
|
||||
(self.xy_block_width, self.xy_block_height) = self.compute_and_verify_intrablock_dimensions(self.xy_block)
|
||||
if(self.x_block):
|
||||
debug.check(self.core_block_width * self.cores_per_x_block == self.x_block_width, "core_block does not align with x_block")
|
||||
if(self.y_block):
|
||||
debug.check(self.core_block_height * self.cores_per_y_block == self.y_block_height, "core_block does not aligns with y_block")
|
||||
if(self.xy_block):
|
||||
debug.check(self.xy_block_height == self.x_block_height, "xy_block does not align with x_block")
|
||||
debug.check(self.xy_block_width == self.y_block_width, "xy_block does not align with y_block")
|
||||
|
||||
|
||||
def connect_block(self, block: block, col: int, row: int):
|
||||
for dr in range(len(block)):
|
||||
row_done = False
|
||||
for dc in range(len(block[0])):
|
||||
if(self.bit_rows.count(self.num_rows) <= self.num_cols and self.bit_cols.count(self.num_cols) <= self.num_rows):
|
||||
inst = block[dr][dc]
|
||||
if(len(self.bit_rows) <= col + dc):
|
||||
self.bit_rows.append(0)
|
||||
if(len(self.bit_cols) <= row + dr):
|
||||
self.bit_cols.append(0)
|
||||
if(row_done or self.bit_cols[row+dr] >= self.num_cols):
|
||||
row_done = True
|
||||
continue
|
||||
if((self.bit_rows[col+dc] < self.num_rows) and (self.bit_cols[row+dr] < self.num_cols)):
|
||||
if(inst.is_bitcell):
|
||||
self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc))
|
||||
self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.cell_inst[self.bit_rows[col+dc], self.bit_cols[row+dr]]
|
||||
self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col+dc], self.bit_cols[row+dr]))
|
||||
self.bit_rows[col+dc] += 1
|
||||
self.bit_cols[row+dr] += 1
|
||||
|
||||
else:
|
||||
self.parent_design.all_inst[row + dr, col + dc] = self.parent_design.add_existing_inst(inst,self.name_template.format(row +dr, col+dc))
|
||||
self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col+dc], self.bit_cols[row+dr]))
|
||||
else:
|
||||
row_done = True
|
||||
|
||||
def connect_array(self) -> None:
|
||||
|
||||
row = 0
|
||||
col = 0
|
||||
for i in range(self.num_cores_y):
|
||||
for j in range (self.num_cores_x):
|
||||
self.connect_block(self.core_block, col, row)
|
||||
col += len(self.core_block[0])
|
||||
col = 0
|
||||
row += len(self.core_block)
|
||||
|
||||
def connect_array_raw(self) -> None:
|
||||
for row in range(self.num_rows):
|
||||
for col in range(self.num_cols):
|
||||
inst = self.core_block[row][col]
|
||||
if(len(self.bit_rows) <= col):
|
||||
self.bit_rows.append(0)
|
||||
if(len(self.bit_cols) <= row):
|
||||
self.bit_cols.append(0)
|
||||
if(inst.is_bitcell):
|
||||
self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col))
|
||||
self.parent_design.all_inst[row, col] = self.parent_design.cell_inst[self.bit_rows[col], self.bit_cols[row]]
|
||||
self.parent_design.connect_inst(self.parent_design.get_bitcell_pins(self.bit_rows[col], self.bit_cols[row]))
|
||||
self.bit_rows[col] += 1
|
||||
self.bit_cols[row] += 1
|
||||
|
||||
else:
|
||||
self.parent_design.all_inst[row, col] = self.parent_design.add_existing_inst(inst,self.name_template.format(row, col))
|
||||
self.parent_design.connect_inst(self.parent_design.get_strap_pins(self.bit_rows[col], self.bit_cols[row]))
|
||||
|
||||
def place_inst(self, inst, offset) -> None:
|
||||
x = offset[0]
|
||||
y = offset[1]
|
||||
if "X" in inst.mirror:
|
||||
y += inst.height
|
||||
if "Y" in inst.mirror:
|
||||
x += inst.width
|
||||
#print('placing inst {} at {}'.format(inst, offset))
|
||||
inst.place((x, y), inst.mirror, inst.rotate)
|
||||
|
||||
def place_array(self):
|
||||
|
||||
(self.row_max, self.col_max) = list(self.parent_design.all_inst.keys())[-1]
|
||||
y = 0
|
||||
for row in range(self.row_max+1):
|
||||
x = 0
|
||||
for col in range(self.col_max+1):
|
||||
inst = self.parent_design.all_inst[row, col]
|
||||
self.place_inst(inst, (x, y))
|
||||
x += inst.width
|
||||
y += inst.height
|
||||
|
||||
self.parent_design.width = max([x.rx() for x in self.parent_design.insts])
|
||||
self.parent_design.height = max([x.uy() for x in self.parent_design.insts])
|
||||
|
||||
def append_row_to_block(block, row):
|
||||
block.append(row)
|
||||
|
||||
def append_block_under_block(base_block, under_block):
|
||||
base_block = base_block + under_block
|
||||
|
||||
def append_block_right_block(base_block, right_block):
|
||||
for row in base_block:
|
||||
row = row + right_block
|
||||
|
||||
def rotate_list(lst, n):
|
||||
# Loop through the range of n positions to rotate
|
||||
for i in range(n):
|
||||
# Remove the last element of the list and insert it at the beginning
|
||||
lst.insert(0, lst.pop())
|
||||
# Return the rotated list
|
||||
return lst
|
||||
@@ -156,13 +156,15 @@ class pgate(design):
|
||||
|
||||
return via
|
||||
|
||||
def extend_wells(self):
|
||||
def extend_wells(self, width=None):
|
||||
""" Extend the n/p wells to cover whole cell """
|
||||
|
||||
# This should match the cells in the cell library
|
||||
self.nwell_yoffset = 0.48 * self.height
|
||||
full_height = self.height + 0.5 * self.m1_width
|
||||
|
||||
if width == None:
|
||||
width = self.width + 2 * self.well_extend_active
|
||||
|
||||
# FIXME: float rounding problem
|
||||
if "nwell" in layer:
|
||||
@@ -173,12 +175,12 @@ class pgate(design):
|
||||
nwell_height = nwell_max_offset - self.nwell_yoffset
|
||||
self.add_rect(layer="nwell",
|
||||
offset=nwell_position,
|
||||
width=self.width + 2 * self.well_extend_active,
|
||||
width=width,
|
||||
height=nwell_height)
|
||||
if "vtg" in layer:
|
||||
self.add_rect(layer="vtg",
|
||||
offset=nwell_position,
|
||||
width=self.width + 2 * self.well_extend_active,
|
||||
width=width,
|
||||
height=nwell_height)
|
||||
|
||||
# Start this half a rail width below the cell
|
||||
@@ -189,12 +191,12 @@ class pgate(design):
|
||||
pwell_height = self.nwell_yoffset - pwell_position.y
|
||||
self.add_rect(layer="pwell",
|
||||
offset=pwell_position,
|
||||
width=self.width + 2 * self.well_extend_active,
|
||||
width=width,
|
||||
height=pwell_height)
|
||||
if "vtg" in layer:
|
||||
self.add_rect(layer="vtg",
|
||||
offset=pwell_position,
|
||||
width=self.width + 2 * self.well_extend_active,
|
||||
width=width,
|
||||
height=pwell_height)
|
||||
|
||||
if cell_props.pgate.add_implants:
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
# All rights reserved.
|
||||
#
|
||||
from openram import debug
|
||||
from openram.base import round_to_grid
|
||||
from openram.tech import drc
|
||||
from openram.base import vector
|
||||
from openram.base import contact
|
||||
from openram.sram_factory import factory
|
||||
@@ -22,8 +24,8 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
Requires a regular bitcell array and (if using replica topology)
|
||||
replica bitcell and dummy bitcell (BL/BR disconnected).
|
||||
"""
|
||||
def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, name=""):
|
||||
super().__init__(name=name, rows=rows, cols=cols, column_offset=0)
|
||||
def __init__(self, rows, cols, rbl=None, left_rbl=None, right_rbl=None, column_offset=0, row_offset=0, name=""):
|
||||
super().__init__(name=name, rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset)
|
||||
debug.info(1, "Creating {0} {1} x {2} rbls: {3} left_rbl: {4} right_rbl: {5}".format(self.name,
|
||||
rows,
|
||||
cols,
|
||||
@@ -33,6 +35,9 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
|
||||
self.add_comment("rbl: {0} left_rbl: {1} right_rbl: {2}".format(rbl, left_rbl, right_rbl))
|
||||
|
||||
self.column_offset=column_offset
|
||||
self.row_offset=row_offset
|
||||
|
||||
self.column_size = cols
|
||||
self.row_size = rows
|
||||
# This is how many RBLs are in all the arrays
|
||||
@@ -74,9 +79,12 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
""" Array and dummy/replica columns """
|
||||
# Bitcell array
|
||||
self.bitcell_array = factory.create(module_type="bitcell_array",
|
||||
column_offset=1 + len(self.left_rbl),
|
||||
column_offset=len(self.left_rbl)+ self.column_offset,
|
||||
row_offset=len(self.left_rbl)+ self.row_offset,
|
||||
cols=self.column_size,
|
||||
rows=self.row_size)
|
||||
rows=self.row_size,
|
||||
left_rbl=self.left_rbl,
|
||||
right_rbl=self.right_rbl)
|
||||
|
||||
# Replica bitlines
|
||||
self.replica_columns = {}
|
||||
@@ -87,29 +95,36 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
if port in self.left_rbl:
|
||||
# These go top down starting from the bottom of the bitcell array.
|
||||
replica_bit = self.rbl[0] - port - 1
|
||||
column_offset = len(self.left_rbl)
|
||||
rbc_offset = 0
|
||||
elif port in self.right_rbl:
|
||||
# These go bottom up starting from the top of the bitcell array.
|
||||
replica_bit = self.rbl[0] + self.row_size + port - 1
|
||||
column_offset = len(self.left_rbl) + self.column_size + 1
|
||||
rbc_offset = len(self.left_rbl) + self.column_size
|
||||
else:
|
||||
continue
|
||||
|
||||
self.replica_columns[port] = factory.create(module_type="replica_column",
|
||||
rows=self.row_size,
|
||||
rbl=self.rbl,
|
||||
column_offset=column_offset,
|
||||
column_offset=rbc_offset + self.column_offset,
|
||||
replica_bit=replica_bit)
|
||||
|
||||
# Dummy row (for replica wordlines)
|
||||
self.dummy_row = factory.create(module_type="dummy_array",
|
||||
self.dummy_rows = {}
|
||||
|
||||
for port in self.all_ports:
|
||||
if port in self.left_rbl:
|
||||
dummy_offset = 0
|
||||
elif port in self.right_rbl:
|
||||
dummy_offset = self.row_size + len(self.left_rbl)
|
||||
else:
|
||||
dummy_offset = 0
|
||||
|
||||
self.dummy_rows[port] = factory.create(module_type="dummy_array",
|
||||
cols=self.column_size,
|
||||
rows=1,
|
||||
# cap column + left replica column
|
||||
# FIXME: these col offsets should really start at 0 because
|
||||
# this is the left edge of the array... but changing them all is work
|
||||
column_offset=1 + len(self.left_rbl),
|
||||
mirror=0)
|
||||
row_offset=dummy_offset + self.row_offset,
|
||||
column_offset=len(self.left_rbl) + self.row_offset)
|
||||
|
||||
def add_pins(self):
|
||||
|
||||
@@ -213,7 +228,7 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
for port in self.all_ports: # TODO: tie to self.rbl or whatever
|
||||
if self.rbl[port] != 0:
|
||||
self.dummy_row_replica_insts.append(self.add_inst(name="dummy_row_{}".format(port),
|
||||
mod=self.dummy_row))
|
||||
mod=self.dummy_rows[port]))
|
||||
self.connect_inst(self.all_bitline_names + self.rbl_wordline_names[port] + self.supplies)
|
||||
else:
|
||||
self.dummy_row_replica_insts.append(None)
|
||||
@@ -225,28 +240,28 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
self.vertical_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[0]))
|
||||
self.horizontal_pitch = 1.1 * getattr(self, "{}_pitch".format(self.supply_stack[2]))
|
||||
|
||||
# This is a bitcell x bitcell offset to scale
|
||||
self.bitcell_offset = vector(self.cell.width, self.cell.height)
|
||||
self.col_end_offset = vector(self.cell.width, self.cell.height)
|
||||
self.row_end_offset = vector(self.cell.width, self.cell.height)
|
||||
|
||||
# Everything is computed with the main array
|
||||
self.bitcell_array_inst.place(offset=0)
|
||||
self.bitcell_array_inst.place(offset=(0,0))
|
||||
|
||||
self.add_replica_columns()
|
||||
|
||||
# Array was at (0, 0) but move everything so it is at the lower left
|
||||
# We move DOWN the number of left RBL even if we didn't add the column to this bitcell array
|
||||
# Note that this doesn't include the row/col cap
|
||||
array_offset = self.bitcell_offset.scale(-len(self.left_rbl), -self.rbl[0])
|
||||
self.translate_all(array_offset)
|
||||
|
||||
#rbc_width = (self.replica_col_insts[0].width, 0)
|
||||
#dummy_height = max(x for x in map(lambda x: x if x != None else 0, self.))
|
||||
#array_offset = self.bitcell_offset.scale(-len(self.left_rbl), -self.rbl[0])
|
||||
ll=vector(min([x.lx() for x in self.insts]),min([y.by() for y in self.insts]))
|
||||
|
||||
self.translate_all(ll)
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
self.route_supplies()
|
||||
|
||||
self.height = (sum(self.rbl) + self.row_size) * self.cell.height
|
||||
self.width = (len(self.rbls) + self.column_size) * self.cell.width
|
||||
|
||||
self.width = max([x.rx() for x in self.insts]) - min([x.lx() for x in self.insts])
|
||||
self.height = max([x.uy() for x in self.insts]) - min([y.by() for y in self.insts])
|
||||
|
||||
self.add_boundary()
|
||||
|
||||
@@ -280,25 +295,24 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
|
||||
# Grow from left to right, toward the array
|
||||
for bit, port in enumerate(self.left_rbl):
|
||||
offset = self.bitcell_offset.scale(-len(self.left_rbl) + bit, -self.rbl[0])
|
||||
rbc_dimension = vector(self.replica_col_insts[port].width, self.cell.height)
|
||||
offset = rbc_dimension.scale(-len(self.left_rbl) + bit, -self.rbl[0])
|
||||
self.replica_col_insts[bit].place(offset)
|
||||
# Grow to the right of the bitcell array, array outward
|
||||
for bit, port in enumerate(self.right_rbl):
|
||||
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.rbl[0])
|
||||
offset = self.bitcell_array_inst.lr() + rbc_dimension.scale(bit, -self.rbl[0])
|
||||
self.replica_col_insts[self.rbl[0] + bit].place(offset)
|
||||
|
||||
# Replica dummy rows
|
||||
# Add the dummy rows even if we aren't adding the replica column to this bitcell array
|
||||
# These grow up, toward the array
|
||||
for bit in range(self.rbl[0]):
|
||||
dummy_offset = self.bitcell_offset.scale(0, -self.rbl[0] + bit + (-self.rbl[0] + bit) % 2)
|
||||
self.dummy_row_replica_insts[bit].place(offset=dummy_offset,
|
||||
mirror="MX" if (-self.rbl[0] + bit) % 2 else "R0")
|
||||
dummy_offset = self.bitcell_array_inst.ll() - vector(0, self.dummy_row_replica_insts[bit].height)
|
||||
self.dummy_row_replica_insts[bit].place(offset=dummy_offset)
|
||||
# These grow up, away from the array
|
||||
for bit in range(self.rbl[1]):
|
||||
dummy_offset = self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul()
|
||||
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset,
|
||||
mirror="MX" if (self.row_size + bit) % 2 else "R0")
|
||||
dummy_offset = self.bitcell_array_inst.ul()
|
||||
self.dummy_row_replica_insts[self.rbl[0] + bit].place(offset=dummy_offset)
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
@@ -316,14 +330,15 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
|
||||
# Replica wordlines (go by the row instead of replica column because we may have to add a pin
|
||||
# even though the column is in another local bitcell array)
|
||||
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
|
||||
for (wl_name, pin_name) in zip(names, self.dummy_row.get_wordline_names()):
|
||||
pin = inst.get_pin(pin_name)
|
||||
self.add_layout_pin(text=wl_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=pin.height())
|
||||
if self.rbl != [0,0]:
|
||||
for (names, inst) in zip(self.rbl_wordline_names, self.dummy_row_replica_insts):
|
||||
for (wl_name, pin_name) in zip(names, self.dummy_rows[0].get_wordline_names()):
|
||||
pin = inst.get_pin(pin_name)
|
||||
self.add_layout_pin(text=wl_name,
|
||||
layer=pin.layer,
|
||||
offset=pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=pin.height())
|
||||
|
||||
# Main array bl/br
|
||||
for pin_name in self.all_bitline_names:
|
||||
@@ -347,12 +362,6 @@ class replica_bitcell_array(bitcell_base_array):
|
||||
width=pin.width(),
|
||||
height=self.height)
|
||||
|
||||
def route_supplies(self):
|
||||
""" just copy supply pins from all instances """
|
||||
for inst in self.insts:
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
# Dynamic Power from Bitline
|
||||
|
||||
@@ -9,7 +9,8 @@ from openram.sram_factory import factory
|
||||
from openram.tech import layer_properties as layer_props
|
||||
from openram import OPTS
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
|
||||
from openram.base import geometry
|
||||
from openram.modules import pattern
|
||||
|
||||
class replica_column(bitcell_base_array):
|
||||
"""
|
||||
@@ -29,7 +30,7 @@ class replica_column(bitcell_base_array):
|
||||
self.row_start = rbl[0]
|
||||
# End of regular word line rows
|
||||
self.row_end = self.row_start + rows
|
||||
super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, name=name)
|
||||
super().__init__(rows=self.row_size, cols=1, column_offset=column_offset, row_offset=0, name=name)
|
||||
|
||||
self.rows = rows
|
||||
self.left_rbl = rbl[0]
|
||||
@@ -44,10 +45,10 @@ class replica_column(bitcell_base_array):
|
||||
debug.check(replica_bit < self.row_start or replica_bit >= self.row_end,
|
||||
"Replica bit cannot be in the regular array.")
|
||||
|
||||
if layer_props.replica_column.even_rows:
|
||||
debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0,
|
||||
"sky130 currently requires rows to be even and to start with X mirroring"
|
||||
+ " (left_rbl must be odd) for LVS.")
|
||||
#if layer_props.replica_column.even_rows:
|
||||
# debug.check(rows % 2 == 0 and (self.left_rbl + 1) % 2 == 0,
|
||||
# "sky130 currently requires rows to be even and to start with X mirroring"
|
||||
# + " (left_rbl must be odd) for LVS.")
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
@@ -59,10 +60,7 @@ class replica_column(bitcell_base_array):
|
||||
self.create_instances()
|
||||
|
||||
def create_layout(self):
|
||||
self.place_instances()
|
||||
|
||||
self.height = self.cell_inst[-1].uy()
|
||||
self.width = self.cell_inst[0].rx()
|
||||
self.place_array()
|
||||
|
||||
self.add_layout_pins()
|
||||
|
||||
@@ -88,103 +86,38 @@ class replica_column(bitcell_base_array):
|
||||
self.dummy_cell = factory.create(module_type=OPTS.dummy_bitcell)
|
||||
|
||||
def create_instances(self):
|
||||
self.cell_inst = []
|
||||
self.cell_inst = {}
|
||||
core_block = [[0 for x in range(1)] for y in range(self.total_size)]
|
||||
|
||||
current_row = self.row_start
|
||||
for row in range(self.total_size):
|
||||
name = "rbc_{0}".format(row)
|
||||
|
||||
# Regular array cells are replica cells
|
||||
# Replic bit specifies which other bit (in the full range (0,total_size) to make a replica cell.
|
||||
# All other cells are dummies
|
||||
|
||||
if (row == self.replica_bit) or (row >= self.row_start and row < self.row_end):
|
||||
self.cell_inst.append(self.add_inst(name=name,
|
||||
mod=self.replica_cell))
|
||||
self.connect_inst(self.get_bitcell_pins(row, 0))
|
||||
if current_row % 2 == 0:
|
||||
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='MY')
|
||||
else:
|
||||
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.replica_cell, is_bitcell=True, mirror='XY')
|
||||
else:
|
||||
self.cell_inst.append(self.add_inst(name=name,
|
||||
mod=self.dummy_cell))
|
||||
self.connect_inst(self.get_bitcell_pins(row, 0))
|
||||
if current_row % 2 == 0:
|
||||
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='MY')
|
||||
else:
|
||||
core_block[row][0] = geometry.instance("rbc_{}".format(row), mod=self.dummy_cell, is_bitcell=True, mirror='XY')
|
||||
|
||||
def place_instances(self):
|
||||
# Flip the mirrors if we have an odd number of replica+dummy rows at the bottom
|
||||
# so that we will start with mirroring rather than not mirroring
|
||||
rbl_offset = (self.left_rbl) % 2
|
||||
|
||||
# if our bitcells are mirrored on the y axis, check if we are in global
|
||||
# column that needs to be flipped.
|
||||
dir_y = False
|
||||
xoffset = 0
|
||||
if self.cell.mirror.y and self.column_offset % 2:
|
||||
dir_y = True
|
||||
xoffset = self.replica_cell.width
|
||||
|
||||
for row in range(self.total_size):
|
||||
# name = "bit_r{0}_{1}".format(row, "rbl")
|
||||
dir_x = self.cell.mirror.x and (row + rbl_offset) % 2
|
||||
|
||||
offset = vector(xoffset, self.cell.height * (row + (row + rbl_offset) % 2))
|
||||
|
||||
if dir_x and dir_y:
|
||||
dir_key = "XY"
|
||||
elif dir_x:
|
||||
dir_key = "MX"
|
||||
elif dir_y:
|
||||
dir_key = "MY"
|
||||
else:
|
||||
dir_key = ""
|
||||
|
||||
self.cell_inst[row].place(offset=offset,
|
||||
mirror=dir_key)
|
||||
|
||||
def add_layout_pins(self):
|
||||
for port in self.all_ports:
|
||||
bl_pin = self.cell_inst[0].get_pin(self.cell.get_bl_name(port))
|
||||
self.add_layout_pin(text="bl_{0}_{1}".format(port, 0),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll().scale(1, 0),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
bl_pin = self.cell_inst[0].get_pin(self.cell.get_br_name(port))
|
||||
self.add_layout_pin(text="br_{0}_{1}".format(port, 0),
|
||||
layer=bl_pin.layer,
|
||||
offset=bl_pin.ll().scale(1, 0),
|
||||
width=bl_pin.width(),
|
||||
height=self.height)
|
||||
|
||||
for port in self.all_ports:
|
||||
current_row += 1
|
||||
|
||||
if self.cell.mirror.y:
|
||||
for row in range(self.total_size):
|
||||
wl_pin = self.cell_inst[row].get_pin(self.cell.get_wl_name(port))
|
||||
self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
|
||||
layer=wl_pin.layer,
|
||||
offset=wl_pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
if self.column_offset % 2 == 0:
|
||||
if core_block[row][0].mirror=='MY':
|
||||
core_block[row][0].mirror=''
|
||||
else:
|
||||
core_block[row][0].mirror='MX'
|
||||
|
||||
def route_supplies(self):
|
||||
|
||||
for inst in self.cell_inst:
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(inst, pin_name)
|
||||
|
||||
def get_bitline_names(self, port=None):
|
||||
if port == None:
|
||||
return self.all_bitline_names
|
||||
else:
|
||||
return self.bitline_names[port]
|
||||
|
||||
def get_bitcell_pins(self, row, col):
|
||||
"""
|
||||
Creates a list of connections in the bitcell,
|
||||
indexed by column and row, for instance use in bitcell_array
|
||||
"""
|
||||
bitcell_pins = []
|
||||
for port in self.all_ports:
|
||||
bitcell_pins.extend([x for x in self.get_bitline_names(port) if x.endswith("_{0}".format(col))])
|
||||
bitcell_pins.extend([x for x in self.all_wordline_names if x.endswith("_{0}".format(row))])
|
||||
bitcell_pins.append("vdd")
|
||||
bitcell_pins.append("gnd")
|
||||
|
||||
return bitcell_pins
|
||||
self.pattern = pattern(self, "bitcell_array", core_block, num_rows=self.total_size, num_cols=self.column_size, name_template="rbc_r{0}_c{1}")
|
||||
self.pattern.connect_array()
|
||||
|
||||
def get_bitcell_pins_col_cap(self, row, col):
|
||||
"""
|
||||
@@ -208,4 +141,4 @@ class replica_column(bitcell_base_array):
|
||||
|
||||
for row, cell in enumerate(self.cell_inst):
|
||||
if row != self.replica_bit:
|
||||
self.graph_inst_exclude.add(cell)
|
||||
self.graph_inst_exclude.add(self.cell_inst[cell])
|
||||
|
||||
@@ -6,16 +6,21 @@
|
||||
from openram.sram_factory import factory
|
||||
from openram import OPTS
|
||||
from .bitcell_base_array import bitcell_base_array
|
||||
|
||||
from .pattern import pattern
|
||||
from openram.base import geometry
|
||||
from math import ceil
|
||||
|
||||
class row_cap_array(bitcell_base_array):
|
||||
"""
|
||||
Generate a dummy row/column for the replica array.
|
||||
"""
|
||||
def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
|
||||
def __init__(self, rows, cols, column_offset=0, row_offset=0, mirror=0, location="", name=""):
|
||||
super().__init__(rows=rows, cols=cols, column_offset=column_offset, row_offset=row_offset, name=name)
|
||||
self.mirror = mirror
|
||||
self.no_instances = True
|
||||
self.location = location
|
||||
self.row_offset = row_offset
|
||||
self.column_offset = column_offset
|
||||
#self.no_instances = True
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
@@ -32,7 +37,7 @@ class row_cap_array(bitcell_base_array):
|
||||
|
||||
def create_layout(self):
|
||||
|
||||
self.place_array("dummy_r{0}_c{1}", self.mirror)
|
||||
self.place_array()
|
||||
self.add_layout_pins()
|
||||
|
||||
self.width = max([x.rx() for x in self.insts])
|
||||
@@ -43,19 +48,41 @@ class row_cap_array(bitcell_base_array):
|
||||
|
||||
def add_modules(self):
|
||||
""" Add the modules used in this design """
|
||||
self.dummy_cell = factory.create(module_type="row_cap_{}".format(OPTS.bitcell))
|
||||
self.row_cap = factory.create(module_type="row_cap_{}".format(OPTS.bitcell))
|
||||
|
||||
self.cell = factory.create(module_type=OPTS.bitcell)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create the module instances used in this design """
|
||||
self.cell_inst = {}
|
||||
for col in range(self.column_size):
|
||||
for row in range(0, self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.dummy_cell)
|
||||
self.connect_inst(self.get_bitcell_pins(row, col))
|
||||
self.all_inst={}
|
||||
self.cell_inst={}
|
||||
|
||||
bit_block = []
|
||||
|
||||
if self.column_offset % 2 == 0:
|
||||
#top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False, mirror="MY")
|
||||
#bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="XY")
|
||||
rowend = geometry.instance("row_cap_rowend", mod=self.row_cap, is_bitcell=True, mirror="MX")
|
||||
rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="")
|
||||
else:
|
||||
#top_corner = geometry.instance("row_cap_top_corner", mod=self.top_corner, is_bitcell=False)
|
||||
#bottom_corner = geometry.instance("row_cap_bottom_corner", mod=self.bottom_corner, is_bitcell=False, mirror="MX")
|
||||
rowend = geometry.instance("row_cap_rowend", mod=self.row_cap, is_bitcell=True, mirror="XY")
|
||||
rowend_m = geometry.instance("row_cap_rowend_m", mod=self.row_cap, is_bitcell=True, mirror="MY")
|
||||
#pattern.append_row_to_block(bit_block, [top_corner])
|
||||
for row in range(0, self.row_size):
|
||||
if row % 2 == 0:
|
||||
pattern.append_row_to_block(bit_block, [rowend])
|
||||
else:
|
||||
pattern.append_row_to_block(bit_block, [rowend_m])
|
||||
|
||||
#pattern.append_row_to_block(bit_block, [bottom_corner])
|
||||
if self.cell.has_corners is False:
|
||||
num_rows = self.row_size - 2
|
||||
else:
|
||||
num_rows = self.row_size
|
||||
self.pattern = pattern(self, "row_cap_array_" + self.location, bit_block, num_rows=num_rows, num_cols=self.column_size, num_cores_x=ceil(self.column_size/2), num_cores_y=ceil(self.row_size/2), name_template="row_cap_array" + self.location + "_r{0}_c{1}")
|
||||
self.pattern.connect_array_raw()
|
||||
|
||||
def get_bitcell_pins(self, row, col):
|
||||
"""
|
||||
@@ -69,44 +96,21 @@ class row_cap_array(bitcell_base_array):
|
||||
|
||||
return bitcell_pins
|
||||
|
||||
def place_array(self, name_template, row_offset=0):
|
||||
xoffset = 0.0
|
||||
for col in range(self.column_size):
|
||||
yoffset = self.cell.height
|
||||
tempx, dir_y = self._adjust_x_offset(xoffset, col, self.column_offset)
|
||||
|
||||
for row in range(self.row_size):
|
||||
tempy, dir_x = self._adjust_y_offset(yoffset, row + 1, row_offset)
|
||||
|
||||
if dir_x and dir_y:
|
||||
dir_key = "XY"
|
||||
elif dir_x:
|
||||
dir_key = "MX"
|
||||
elif dir_y:
|
||||
dir_key = "MY"
|
||||
else:
|
||||
dir_key = ""
|
||||
|
||||
self.cell_inst[row, col].place(offset=[tempx, tempy],
|
||||
mirror=dir_key)
|
||||
yoffset += self.cell.height
|
||||
xoffset += self.cell.width
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add the layout pins """
|
||||
|
||||
row_list = self.cell.get_all_wl_names()
|
||||
|
||||
for row in range(1, self.row_size - 1):
|
||||
for cell_row in row_list:
|
||||
wl_pin = self.cell_inst[row, 0].get_pin(cell_row)
|
||||
self.add_layout_pin(text=cell_row + "_{0}".format(row),
|
||||
wl_names = self.cell.get_all_wl_names()
|
||||
max_row = self.row_size - 2
|
||||
for row in range(0, max_row):
|
||||
for port in self.all_ports:
|
||||
wl_pin = self.cell_inst[row, 0].get_pin(wl_names[port])
|
||||
self.add_layout_pin(text="wl_{0}_{1}".format(port, row),
|
||||
layer=wl_pin.layer,
|
||||
offset=wl_pin.ll().scale(0, 1),
|
||||
width=self.width,
|
||||
height=wl_pin.height())
|
||||
|
||||
for row in range(1, self.row_size - 1):
|
||||
for row in range(0, max_row):
|
||||
for col in range(self.column_size):
|
||||
inst = self.cell_inst[row, col]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
|
||||
@@ -662,7 +662,10 @@ class sram_1bank(design, verilog, lef):
|
||||
inputs = []
|
||||
outputs = []
|
||||
for bit in range(self.num_spare_cols):
|
||||
inputs.append("spare_wen{}[{}]".format(port, bit))
|
||||
if self.num_spare_cols == 1:
|
||||
inputs.append("spare_wen{}".format(port))
|
||||
else:
|
||||
inputs.append("spare_wen{}[{}]".format(port, bit))
|
||||
outputs.append("bank_spare_wen{}_{}".format(port, bit))
|
||||
|
||||
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
|
||||
|
||||
@@ -11,6 +11,7 @@ from openram.base import design
|
||||
from openram.base import vector
|
||||
from openram.sram_factory import factory
|
||||
from openram import OPTS
|
||||
from openram.tech import drc
|
||||
|
||||
|
||||
class write_mask_and_array(design):
|
||||
@@ -104,6 +105,10 @@ class write_mask_and_array(design):
|
||||
base = vector(self.offsets[int(i * write_bits)], 0)
|
||||
self.and2_insts[i].place(base)
|
||||
|
||||
# decide whethre to connect nwell to avoid drc errors
|
||||
if self.bitcell.width < drc("nwell_to_nwell"):
|
||||
self.and2.extend_wells(width=self.and2.width + drc("nwell_to_nwell"))
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
# Create the enable pin that connects all write mask AND array's B pins
|
||||
|
||||
+3
-3
@@ -151,9 +151,9 @@ class options(optparse.Values):
|
||||
###################
|
||||
# Top process that was ran (openram, memchar, memfunc)
|
||||
top_process = None
|
||||
# Use conda to install the default tools
|
||||
# (existing tools will be used if disabled)
|
||||
use_conda = True
|
||||
# Use Nix to initialize the default open-source toolchain.
|
||||
# If disabled, OpenRAM uses whatever tools are already in PATH.
|
||||
use_nix = True
|
||||
# Variable to select the variant of spice
|
||||
spice_name = None
|
||||
# The spice executable being used which is derived from the user PATH.
|
||||
|
||||
@@ -141,12 +141,12 @@ class sram_config:
|
||||
+ " Bank addr size: {}".format(self.bank_addr_size))
|
||||
|
||||
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports
|
||||
if num_ports == 1:
|
||||
if ((self.num_cols + num_ports + self.num_spare_cols) % self.array_col_multiple != 0):
|
||||
debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, self.array_col_multiple), -1)
|
||||
|
||||
if ((self.num_rows + num_ports) % self.array_row_multiple != 0):
|
||||
debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, self.array_row_multiple), -1)
|
||||
#if num_ports == 1:
|
||||
# if ((self.num_cols + num_ports + self.num_spare_cols) % self.array_col_multiple != 0):
|
||||
# debug.error("Invalid number of cols including rbl(s): {}. Total cols must be divisible by {}".format(self.num_cols + num_ports + self.num_spare_cols, self.array_col_multiple), -1)#
|
||||
#
|
||||
# if ((self.num_rows + num_ports) % self.array_row_multiple != 0):
|
||||
# debug.error("invalid number of rows including dummy row(s): {}. Total cols must be divisible by {}".format(self.num_rows + num_ports, self.array_row_multiple), -1)
|
||||
|
||||
def estimate_words_per_row(self, tentative_num_cols, word_size):
|
||||
"""
|
||||
|
||||
@@ -31,7 +31,7 @@ class array_test(openram_test):
|
||||
num_spare_rows = 0
|
||||
num_spare_cols = 0
|
||||
|
||||
a = factory.create(module_type="bitcell_array", cols=8 + num_spare_cols, rows=8 + num_spare_rows)
|
||||
a = factory.create(module_type="bitcell_array", cols=2 + num_spare_cols, rows=2 + num_spare_rows)
|
||||
self.local_check(a)
|
||||
|
||||
openram.end_openram()
|
||||
|
||||
@@ -26,7 +26,7 @@ class capped_replica_bitcell_array_norbl_1rw_test(openram_test):
|
||||
openram.setup_bitcell()
|
||||
|
||||
debug.info(2, "Testing 7x5 capped replica array for 1rw cell without replica column or dummy row")
|
||||
a = factory.create(module_type="capped_replica_bitcell_array", cols=7, rows=5, rbl=[0, 0])
|
||||
a = factory.create(module_type="capped_replica_bitcell_array", cols=8, rows=6, rbl=[0, 0])
|
||||
self.local_check(a)
|
||||
|
||||
openram.end_openram()
|
||||
|
||||
@@ -23,6 +23,7 @@ class replica_bitcell_array_leftrbl_1rw_test(openram_test):
|
||||
OPTS.num_rw_ports = 1
|
||||
OPTS.num_r_ports = 0
|
||||
OPTS.num_w_ports = 0
|
||||
|
||||
openram.setup_bitcell()
|
||||
|
||||
debug.info(2, "Testing 7x5 replica array for 1rw cell with left replica column")
|
||||
|
||||
@@ -24,7 +24,9 @@ class single_bank_test(openram_test):
|
||||
from openram import sram_config
|
||||
|
||||
c = sram_config(word_size=4,
|
||||
num_words=16)
|
||||
num_words=16,
|
||||
num_spare_cols=1,
|
||||
num_spare_rows=1)
|
||||
|
||||
c.num_words=32
|
||||
c.words_per_row=2
|
||||
|
||||
@@ -25,7 +25,7 @@ class sram_1bank_nomux_func_test(openram_test):
|
||||
OPTS.analytical_delay = False
|
||||
OPTS.netlist_only = True
|
||||
OPTS.trim_netlist = False
|
||||
|
||||
OPTS.num_sim_threads = 1
|
||||
# This is a hack to reload the characterizer __init__ with the spice version
|
||||
from importlib import reload
|
||||
from openram import characterizer
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
SHELL := /bin/bash
|
||||
TOP_DIR := $(realpath $(dir $(lastword $(MAKEFILE_LIST)))../..)
|
||||
include $(TOP_DIR)/openram.mk
|
||||
|
||||
@@ -175,7 +176,11 @@ $(TEST_BASES):
|
||||
@rm -rf results/$*
|
||||
@rm -rf results/$*.*
|
||||
@mkdir -p results/$*/tmp
|
||||
ifdef TECH_FILE
|
||||
@sh -c "python3 -u $(OPENRAM_DIR)/$(getfile).py -t $(gettech) -k -f $(TECH_FILE) -v $(ARGS) -p $(OPENRAM_DIR)/results/$* > $(OPENRAM_DIR)/results/$*.out 2>&1 && touch $(OPENRAM_DIR)/results/$*.ok || touch $(OPENRAM_DIR)/results/$*.bad"
|
||||
else
|
||||
@sh -c "python3 -u $(OPENRAM_DIR)/$(getfile).py -t $(gettech) -k -v $(ARGS) -p $(OPENRAM_DIR)/results/$* > $(OPENRAM_DIR)/results/$*.out 2>&1 && touch $(OPENRAM_DIR)/results/$*.ok || touch $(OPENRAM_DIR)/results/$*.bad"
|
||||
endif
|
||||
ifdef KEEP
|
||||
@test -f $(TOP_DIR)/compiler/tests/results/$*.ok && echo "$* ... PASS!" || echo "$* ... FAIL!"
|
||||
else
|
||||
|
||||
@@ -833,8 +833,8 @@ Xrca_top_4
|
||||
X0 ll WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
X1 ul Q_bar_float VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X2 BL WL ul VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
X5 VPWR Q_float lr VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X6 ur Q_bar_float VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X7 VGND Q_float ll VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
@@ -977,8 +977,8 @@ Xrca_19
|
||||
X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
@@ -989,8 +989,8 @@ X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
|
||||
@@ -833,8 +833,8 @@ Xrca_top_4
|
||||
X0 ll WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
X1 ul Q_bar_float VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X2 BL WL ul VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X3 ur WL ur VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
*X4 lr WL lr VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
X5 VPWR Q_float lr VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X6 ur Q_bar_float VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X7 VGND Q_float ll VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
@@ -977,8 +977,8 @@ Xrca_19
|
||||
X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
@@ -989,8 +989,8 @@ X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X0 Q_bar WL BR VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
X1 Q Q_bar VGND VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
X2 BL WL Q VNB sky130_fd_pr__special_nfet_pass w=0.14 l=0.15
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.07 l=0.095
|
||||
*X3 Q WL Q VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
*X4 Q_bar WL Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14u l=25n
|
||||
X5 VPWR Q Q_bar VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X6 Q Q_bar VPWR VPB sky130_fd_pr__special_pfet_pass w=0.14 l=0.15
|
||||
X7 VGND Q Q_bar VNB sky130_fd_pr__special_nfet_latch w=0.21 l=0.15
|
||||
|
||||
@@ -29,26 +29,21 @@ def run_script(cell_name, script="lvs"):
|
||||
|
||||
scriptpath = '{0}run_{1}.sh'.format(OPTS.openram_temp, script)
|
||||
|
||||
# Wrap with conda activate & conda deactivate
|
||||
if OPTS.use_conda:
|
||||
from openram import CONDA_HOME
|
||||
with open(scriptpath, "r") as f:
|
||||
script_content = f.readlines()
|
||||
with open(scriptpath, "w") as f:
|
||||
# First line is shebang
|
||||
f.write(script_content[0])
|
||||
# Activate conda using the activate script
|
||||
f.write("source {}/bin/activate\n".format(CONDA_HOME))
|
||||
for line in script_content[1:]:
|
||||
f.write(line)
|
||||
# Deactivate conda at the end
|
||||
f.write("conda deactivate\n")
|
||||
|
||||
debug.info(2, "Starting {}".format(scriptpath))
|
||||
start = time.time()
|
||||
with open(outfile, 'wb') as fo, open(errfile, 'wb') as fe:
|
||||
if OPTS.use_nix:
|
||||
p_cmd = [
|
||||
"nix",
|
||||
"--extra-experimental-features", "nix-command flakes",
|
||||
"develop",
|
||||
"--command",
|
||||
scriptpath,
|
||||
]
|
||||
else:
|
||||
p_cmd = [scriptpath]
|
||||
p = subprocess.Popen(
|
||||
[scriptpath], stdout=fo, stderr=fe, cwd=OPTS.openram_temp)
|
||||
p_cmd, stdout=fo, stderr=fe, cwd=OPTS.openram_temp)
|
||||
|
||||
if echo_cmd_output:
|
||||
tailo = subprocess.Popen([
|
||||
|
||||
Reference in New Issue
Block a user