Merge branch 'dev' into pex

This commit is contained in:
jcirimel 2020-08-17 17:49:41 -07:00
commit 9cecf367ee
75 changed files with 1195 additions and 395 deletions

View File

@ -43,7 +43,7 @@ The OpenRAM compiler has very few dependencies:
If you want to perform DRC and LVS, you will need either: If you want to perform DRC and LVS, you will need either:
+ Calibre (for [FreePDK45]) + Calibre (for [FreePDK45])
+ [Magic] 8.2.79 or higher (for [SCMOS]) + [Magic] 8.3.27 or higher (for [SCMOS])
+ [Netgen] 1.5 (for [SCMOS]) + [Netgen] 1.5 (for [SCMOS])
You must set two environment variables: You must set two environment variables:
@ -81,6 +81,8 @@ We have included the most recent SCN4M_SUBM design rules from [Qflow].
## Docker Image ## Docker Image
**WARNING! Some OpenRAM dependency tools installed in the Docker image are out-of-date.**
We have a pre-configured Ubuntu [Docker](https://www.docker.com/) image We have a pre-configured Ubuntu [Docker](https://www.docker.com/) image
available that has all tools installed for the [SCMOS] process. It is available that has all tools installed for the [SCMOS] process. It is
available at [docker hub](https://hub.docker.com/r/vlsida/openram-ubuntu). available at [docker hub](https://hub.docker.com/r/vlsida/openram-ubuntu).

View File

@ -97,7 +97,7 @@ class channel_route(design.design):
""" """
name = "cr_{0}".format(channel_route.unique_id) name = "cr_{0}".format(channel_route.unique_id)
channel_route.unique_id += 1 channel_route.unique_id += 1
design.design.__init__(self, name) super().__init__(name)
self.netlist = netlist self.netlist = netlist
self.offset = offset self.offset = offset

View File

@ -34,7 +34,7 @@ class contact(hierarchy_design.hierarchy_design):
# This will ignore the name parameter since # This will ignore the name parameter since
# we can guarantee a unique name here # we can guarantee a unique name here
hierarchy_design.hierarchy_design.__init__(self, name) super().__init__(name)
debug.info(4, "create contact object {0}".format(name)) debug.info(4, "create contact object {0}".format(name))
self.add_comment("layers: {0}".format(layer_stack)) self.add_comment("layers: {0}".format(layer_stack))

View File

@ -20,7 +20,7 @@ class design(hierarchy_design):
""" """
def __init__(self, name): def __init__(self, name):
hierarchy_design.__init__(self, name) super().__init__(name)
self.setup_drc_constants() self.setup_drc_constants()
self.setup_layer_constants() self.setup_layer_constants()
@ -230,13 +230,14 @@ class design(hierarchy_design):
self.write_ports = [] self.write_ports = []
# These are the write-only port indices. # These are the write-only port indices.
self.writeonly_ports = [] self.writeonly_ports = []
# These are teh read/write and read-only port indice # These are the read/write and read-only port indices
self.read_ports = [] self.read_ports = []
# These are the read-only port indices. # These are the read-only port indices.
self.readonly_ports = [] self.readonly_ports = []
# These are all the ports # These are all the ports
self.all_ports = list(range(total_ports)) self.all_ports = list(range(total_ports))
# The order is always fixed as RW, W, R
port_number = 0 port_number = 0
for port in range(OPTS.num_rw_ports): for port in range(OPTS.num_rw_ports):
self.readwrite_ports.append(port_number) self.readwrite_ports.append(port_number)

View File

@ -162,7 +162,7 @@ class instance(geometry):
""" """
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0): def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
"""Initializes an instance to represent a module""" """Initializes an instance to represent a module"""
geometry.__init__(self) super().__init__()
debug.check(mirror not in ["R90", "R180", "R270"], debug.check(mirror not in ["R90", "R180", "R270"],
"Please use rotation and not mirroring during instantiation.") "Please use rotation and not mirroring during instantiation.")
@ -414,7 +414,7 @@ class path(geometry):
def __init__(self, lpp, coordinates, path_width): def __init__(self, lpp, coordinates, path_width):
"""Initializes a path for the specified layer""" """Initializes a path for the specified layer"""
geometry.__init__(self) super().__init__()
self.name = "path" self.name = "path"
self.layerNumber = lpp[0] self.layerNumber = lpp[0]
self.layerPurpose = lpp[1] self.layerPurpose = lpp[1]
@ -452,7 +452,7 @@ class label(geometry):
def __init__(self, text, lpp, offset, zoom=-1): def __init__(self, text, lpp, offset, zoom=-1):
"""Initializes a text label for specified layer""" """Initializes a text label for specified layer"""
geometry.__init__(self) super().__init__()
self.name = "label" self.name = "label"
self.text = text self.text = text
self.layerNumber = lpp[0] self.layerNumber = lpp[0]
@ -496,7 +496,7 @@ class rectangle(geometry):
def __init__(self, lpp, offset, width, height): def __init__(self, lpp, offset, width, height):
"""Initializes a rectangular shape for specified layer""" """Initializes a rectangular shape for specified layer"""
geometry.__init__(self) super().__init__()
self.name = "rect" self.name = "rect"
self.layerNumber = lpp[0] self.layerNumber = lpp[0]
self.layerPurpose = lpp[1] self.layerPurpose = lpp[1]

View File

@ -30,7 +30,7 @@ class route(design):
def __init__(self, obj, layer_stack, path, layer_widths=[None,1,None]): def __init__(self, obj, layer_stack, path, layer_widths=[None,1,None]):
name = "route_{0}".format(route.unique_route_id) name = "route_{0}".format(route.unique_route_id)
route.unique_route_id += 1 route.unique_route_id += 1
design.__init__(self, name) super().__init__(name)
debug.info(3, "create route obj {0}".format(name)) debug.info(3, "create route obj {0}".format(name))
self.obj = obj self.obj = obj

View File

@ -41,7 +41,7 @@ class delay(simulation):
""" """
def __init__(self, sram, spfile, corner): def __init__(self, sram, spfile, corner):
simulation.__init__(self, sram, spfile, corner) super().__init__(sram, spfile, corner)
self.targ_read_ports = [] self.targ_read_ports = []
self.targ_write_ports = [] self.targ_write_ports = []
@ -188,9 +188,8 @@ class delay(simulation):
self.sen_meas.meta_str = sram_op.READ_ZERO self.sen_meas.meta_str = sram_op.READ_ZERO
self.sen_meas.meta_add_delay = True self.sen_meas.meta_add_delay = True
self.dout_volt_meas.append(self.sen_meas)
return self.dout_volt_meas + [self.sen_meas]
return self.dout_volt_meas
def create_read_bit_measures(self): def create_read_bit_measures(self):
""" Adds bit measurements for read0 and read1 cycles """ """ Adds bit measurements for read0 and read1 cycles """
@ -1380,7 +1379,7 @@ class delay(simulation):
Return the analytical model results for the SRAM. Return the analytical model results for the SRAM.
""" """
if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0: if OPTS.num_rw_ports > 1 or OPTS.num_w_ports > 0 and OPTS.num_r_ports > 0:
debug.warning("Analytical characterization results are not supported for multiport.") debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay. # Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0'*self.addr_size, 0) self.set_probe('0'*self.addr_size, 0)

View File

@ -24,7 +24,7 @@ class functional(simulation):
""" """
def __init__(self, sram, spfile, corner): def __init__(self, sram, spfile, corner):
simulation.__init__(self, sram, spfile, corner) super().__init__(sram, spfile, corner)
# Seed the characterizer with a constant seed for unit tests # Seed the characterizer with a constant seed for unit tests
if OPTS.is_unit_test: if OPTS.is_unit_test:

View File

@ -198,4 +198,4 @@ class voltage_at_measure(spice_measurement):
meas_name = self.name meas_name = self.name
targ_name = self.targ_name_no_port targ_name = self.targ_name_no_port
return (meas_name,targ_name,time_at) return (meas_name,targ_name,time_at)

View File

@ -26,7 +26,7 @@ class model_check(delay):
""" """
def __init__(self, sram, spfile, corner, custom_delaychain=False): def __init__(self, sram, spfile, corner, custom_delaychain=False):
delay.__init__(self,sram,spfile,corner) super().__init__(sram, spfile, corner)
self.period = tech.spice["feasible_period"] self.period = tech.spice["feasible_period"]
self.create_data_names() self.create_data_names()
self.custom_delaychain=custom_delaychain self.custom_delaychain=custom_delaychain
@ -446,4 +446,4 @@ class model_check(delay):
return name_dict return name_dict

View File

@ -19,7 +19,7 @@ import re
import copy import copy
import importlib import importlib
VERSION = "1.1.5" VERSION = "1.1.6"
NAME = "OpenRAM v{}".format(VERSION) NAME = "OpenRAM v{}".format(VERSION)
USAGE = "openram.py [options] <config file>\nUse -h for help.\n" USAGE = "openram.py [options] <config file>\nUse -h for help.\n"

View File

@ -36,7 +36,7 @@ class bank(design.design):
if name == "": if name == "":
name = "bank_{0}_{1}".format(self.word_size, self.num_words) name = "bank_{0}_{1}".format(self.word_size, self.num_words)
design.design.__init__(self, name) super().__init__(name)
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size, debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,
self.num_words)) self.num_words))
@ -82,7 +82,7 @@ class bank(design.design):
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT") self.add_pin("dout{0}_{1}".format(port, bit), "OUTPUT")
for port in self.all_ports: for port in self.all_ports:
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT") self.add_pin_list(self.bitcell_array.get_rbl_bitline_names(port), "OUTPUT")
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("din{0}_{1}".format(port, bit), "INPUT") self.add_pin("din{0}_{1}".format(port, bit), "INPUT")
@ -113,6 +113,8 @@ class bank(design.design):
def route_layout(self): def route_layout(self):
""" Create routing amoung the modules """ """ Create routing amoung the modules """
self.route_central_bus() self.route_central_bus()
self.route_unused_wordlines()
for port in self.all_ports: for port in self.all_ports:
self.route_bitlines(port) self.route_bitlines(port)
@ -254,7 +256,7 @@ class bank(design.design):
self.port_data_offsets[port] = vector(self.main_bitcell_array_left, self.bitcell_array_top) self.port_data_offsets[port] = vector(self.main_bitcell_array_left, self.bitcell_array_top)
# LOWER RIGHT QUADRANT # LOWER RIGHT QUADRANT
# To the left of the bitcell array # To the right of the bitcell array
x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap x_offset = self.bitcell_array_right + self.port_address.width + self.m2_gap
self.port_address_offsets[port] = vector(x_offset, self.port_address_offsets[port] = vector(x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
@ -374,7 +376,6 @@ class bank(design.design):
rows=self.num_rows) rows=self.num_rows)
self.add_mod(self.port_address) self.add_mod(self.port_address)
self.port_rbl_map = self.all_ports
self.num_rbl = len(self.all_ports) self.num_rbl = len(self.all_ports)
self.bitcell_array = factory.create(module_type="replica_bitcell_array", self.bitcell_array = factory.create(module_type="replica_bitcell_array",
@ -391,24 +392,28 @@ class bank(design.design):
def create_bitcell_array(self): def create_bitcell_array(self):
""" Creating Bitcell Array """ """ Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array", self.bitcell_array_inst=self.add_inst(name="replica_bitcell_array",
mod=self.bitcell_array) mod=self.bitcell_array)
temp = [] temp = []
for col in range(self.num_cols + self.num_spare_cols): rbl_names = self.bitcell_array.get_rbl_bitline_names()
for bitline in self.bitline_names: temp.extend(rbl_names)
temp.append("{0}_{1}".format(bitline, col)) bitline_names = self.bitcell_array.get_bitline_names()
for rbl in range(self.num_rbl): temp.extend(bitline_names)
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(rbl) # Replace RBL wordline with wl_en#
temp.append(rbl_bl_name) wordline_names = self.bitcell_array.get_wordline_names()
rbl_br_name=self.bitcell_array.get_rbl_br_name(rbl)
temp.append(rbl_br_name) rbl_wl_names = []
for row in range(self.num_rows):
for wordline in self.wl_names:
temp.append("{0}_{1}".format(wordline, row))
for port in self.all_ports: for port in self.all_ports:
temp.append("wl_en{0}".format(port)) rbl_wl_names.append(self.bitcell_array.get_rbl_wordline_names(port))
# Rename the RBL WL to the enable name
for port in self.all_ports:
wordline_names = [x.replace(rbl_wl_names[port], "wl_en{0}".format(port)) for x in wordline_names]
# Connect the other RBL WL to gnd
wordline_names = ["gnd" if x.startswith("rbl_wl") else x for x in wordline_names]
# Connect the dummy WL to gnd
wordline_names = ["gnd" if x.startswith("dummy") else x for x in wordline_names]
temp.extend(wordline_names)
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
@ -419,17 +424,14 @@ class bank(design.design):
def create_port_data(self): def create_port_data(self):
""" Creating Port Data """ """ Creating Port Data """
self.port_data_inst = [None] * len(self.all_ports) self.port_data_inst = [None] * len(self.all_ports)
for port in self.all_ports: for port in self.all_ports:
self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port), self.port_data_inst[port]=self.add_inst(name="port_data{}".format(port),
mod=self.port_data[port]) mod=self.port_data[port])
temp = [] temp = []
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) rbl_bl_names = self.bitcell_array.get_rbl_bitline_names(port)
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) temp.extend(rbl_bl_names)
temp.append(rbl_bl_name)
temp.append(rbl_br_name)
for col in range(self.num_cols + self.num_spare_cols): for col in range(self.num_cols + self.num_spare_cols):
temp.append("{0}_{1}".format(self.bl_names[port], col)) temp.append("{0}_{1}".format(self.bl_names[port], col))
temp.append("{0}_{1}".format(self.br_names[port], col)) temp.append("{0}_{1}".format(self.br_names[port], col))
@ -452,7 +454,6 @@ class bank(design.design):
for bit in range(self.num_spare_cols): for bit in range(self.num_spare_cols):
temp.append("bank_spare_wen{0}_{1}".format(port, bit)) temp.append("bank_spare_wen{0}_{1}".format(port, bit))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_port_data(self, offsets): def place_port_data(self, offsets):
@ -477,7 +478,7 @@ class bank(design.design):
temp = [] temp = []
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size)) temp.append("addr{0}_{1}".format(port, bit + self.col_addr_size))
temp.append("wl_en{0}".format(port)) temp.append("wl_en{}".format(port))
for row in range(self.num_rows): for row in range(self.num_rows):
temp.append("{0}_{1}".format(self.wl_names[port], row)) temp.append("{0}_{1}".format(self.wl_names[port], row))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
@ -665,7 +666,7 @@ class bank(design.design):
# Port 0 # Port 0
# The bank is at (0,0), so this is to the left of the y-axis. # The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs # 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - self.m3_pitch, self.min_y_offset) control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - 2 * self.m3_pitch, self.min_y_offset)
# The control bus is routed up to two pitches below the bitcell array # The control bus is routed up to two pitches below the bitcell array
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
self.bus_pins[0] = self.create_bus(layer="m2", self.bus_pins[0] = self.create_bus(layer="m2",
@ -680,7 +681,7 @@ class bank(design.design):
if len(self.all_ports)==2: if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array # The other control bus is routed up to two pitches above the bitcell array
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
control_bus_offset = vector(self.bitcell_array_right + self.m3_pitch, control_bus_offset = vector(self.bitcell_array_right + 2.5 * self.m3_pitch,
self.max_y_offset - control_bus_length) self.max_y_offset - control_bus_length)
# The bus for the right port is reversed so that the rbl_wl is closest to the array # The bus for the right port is reversed so that the rbl_wl is closest to the array
self.bus_pins[1] = self.create_bus(layer="m2", self.bus_pins[1] = self.create_bus(layer="m2",
@ -713,14 +714,13 @@ class bank(design.design):
# connect spare bitlines # connect spare bitlines
for i in range(self.num_spare_cols): for i in range(self.num_spare_cols):
self.connect_bitline(inst1, inst2, inst1_bl_name.format(self.num_cols+i), "spare" + inst2_bl_name.format(i)) self.connect_bitline(inst1, inst2, inst1_bl_name.format(self.num_cols + i), "spare" + inst2_bl_name.format(i))
self.connect_bitline(inst1, inst2, inst1_br_name.format(self.num_cols+i), "spare" + inst2_br_name.format(i)) self.connect_bitline(inst1, inst2, inst1_br_name.format(self.num_cols + i), "spare" + inst2_br_name.format(i))
# Connect the replica bitlines # Connect the replica bitlines
rbl_bl_name=self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]) rbl_bl_names = self.bitcell_array.get_rbl_bitline_names(port)
rbl_br_name=self.bitcell_array.get_rbl_br_name(self.port_rbl_map[port]) for (array_name, data_name) in zip(rbl_bl_names, ["rbl_bl", "rbl_br"]):
self.connect_bitline(inst1, inst2, rbl_bl_name, "rbl_bl") self.connect_bitline(inst1, inst2, array_name, data_name)
self.connect_bitline(inst1, inst2, rbl_br_name, "rbl_br")
def route_port_data_out(self, port): def route_port_data_out(self, port):
""" Add pins for the port data out """ """ Add pins for the port data out """
@ -961,7 +961,35 @@ class bank(design.design):
self.add_label(text=data_name, self.add_label(text=data_name,
layer="m1", layer="m1",
offset=data_pin.center()) offset=data_pin.center())
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
gnd_wl_names = []
# Connect unused RBL WL to gnd
array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")])
dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")])
rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports])
gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names)
for wl_name in gnd_wl_names:
pin = self.bitcell_array_inst.get_pin(wl_name)
pin_layer = pin.layer
layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
left_pin_loc = pin.lc()
right_pin_loc = pin.rc()
# Place the pins a track outside of the array
left_loc = left_pin_loc - vector(layer_pitch, 0)
right_loc = right_pin_loc + vector(layer_pitch, 0)
self.add_power_pin("gnd", left_loc, directions=("H", "H"))
self.add_power_pin("gnd", right_loc, directions=("H", "H"))
# Add a path to connect to the array
self.add_path(pin_layer, [left_loc, left_pin_loc])
self.add_path(pin_layer, [right_loc, right_pin_loc])
def route_control_lines(self, port): def route_control_lines(self, port):
""" Route the control lines of the entire bank """ """ Route the control lines of the entire bank """
@ -973,10 +1001,10 @@ class bank(design.design):
connection.append((self.prefix + "p_en_bar{}".format(port), connection.append((self.prefix + "p_en_bar{}".format(port),
self.port_data_inst[port].get_pin("p_en_bar"))) self.port_data_inst[port].get_pin("p_en_bar")))
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port]) rbl_wl_name = self.bitcell_array.get_rbl_wordline_names(port)
connection.append((self.prefix + "wl_en{}".format(port), connection.append((self.prefix + "wl_en{}".format(port),
self.bitcell_array_inst.get_pin(rbl_wl_name))) self.bitcell_array_inst.get_pin(rbl_wl_name)))
if port in self.write_ports: if port in self.write_ports:
connection.append((self.prefix + "w_en{}".format(port), connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en"))) self.port_data_inst[port].get_pin("w_en")))
@ -1011,7 +1039,7 @@ class bank(design.design):
self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos]) self.add_wire(self.m1_stack, [pin_pos, mid_pos, control_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=control_pos) offset=control_pos)
def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True): def determine_wordline_stage_efforts(self, external_cout, inp_is_rise=True):
"""Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline""" """Get the all the stage efforts for each stage in the path within the bank clk_buf to a wordline"""
# Decoder is assumed to have settled before the negative edge of the clock. # Decoder is assumed to have settled before the negative edge of the clock.

View File

@ -24,7 +24,7 @@ class bank_select(design.design):
""" """
def __init__(self, name="bank_select", port="rw"): def __init__(self, name="bank_select", port="rw"):
design.design.__init__(self, name) super().__init__(name)
self.port = port self.port = port

View File

@ -13,12 +13,11 @@ from sram_factory import factory
class bitcell_array(bitcell_base_array): class bitcell_array(bitcell_base_array):
""" """
Creates a rows x cols array of memory cells. Assumes bit-lines Creates a rows x cols array of memory cells.
and word line is connected by abutment. Assumes bit-lines and word lines are connected by abutment.
Connects the word lines and bit lines.
""" """
def __init__(self, cols, rows, name, column_offset=0): def __init__(self, rows, cols, column_offset=0, name=""):
super().__init__(cols, rows, name, column_offset) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -27,7 +26,7 @@ class bitcell_array(bitcell_base_array):
# We don't offset this because we need to align # We don't offset this because we need to align
# the replica bitcell in the control logic # the replica bitcell in the control logic
# self.offset_all_coordinates() # self.offset_all_coordinates()
def create_netlist(self): def create_netlist(self):
""" Create and connect the netlist """ """ Create and connect the netlist """
self.add_modules() self.add_modules()
@ -41,7 +40,7 @@ class bitcell_array(bitcell_base_array):
self.add_layout_pins() self.add_layout_pins()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_modules(self): def add_modules(self):
@ -57,21 +56,21 @@ class bitcell_array(bitcell_base_array):
name = "bit_r{0}_c{1}".format(row, col) name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name, self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.cell) mod=self.cell)
self.connect_inst(self.get_bitcell_pins(col, row)) self.connect_inst(self.get_bitcell_pins(row, col))
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW.""" """Power of Bitcell array and bitline in nW."""
# Dynamic Power from Bitline # Dynamic Power from Bitline
bl_wire = self.gen_bl_wire() bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap() cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"] freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
# Calculate the bitcell power which currently only includes leakage # Calculate the bitcell power which currently only includes leakage
cell_power = self.cell.analytical_power(corner, load) cell_power = self.cell.analytical_power(corner, load)
# Leakage power grows with entire array and bitlines. # Leakage power grows with entire array and bitlines.
total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size, total_power = self.return_power(cell_power.dynamic + bitline_dynamic * self.column_size,
cell_power.leakage * self.column_size * self.row_size) cell_power.leakage * self.column_size * self.row_size)
@ -83,7 +82,8 @@ class bitcell_array(bitcell_base_array):
else: else:
width = self.width width = self.width
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1")) 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 # 2 access tx gate per cell
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c
return wl_wire return wl_wire
def gen_bl_wire(self): def gen_bl_wire(self):
@ -93,7 +93,8 @@ class bitcell_array(bitcell_base_array):
height = self.height height = self.height
bl_pos = 0 bl_pos = 0
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1")) 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 # 1 access tx d/s per cell
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c
return bl_wire return bl_wire
def get_wordline_cin(self): def get_wordline_cin(self):
@ -102,7 +103,7 @@ class bitcell_array(bitcell_base_array):
bitcell_wl_cin = self.cell.get_wl_cin() bitcell_wl_cin = self.cell.get_wl_cin()
total_cin = bitcell_wl_cin * self.column_size total_cin = bitcell_wl_cin * self.column_size
return total_cin return total_cin
def graph_exclude_bits(self, targ_row, targ_col): def graph_exclude_bits(self, targ_row, targ_col):
"""Excludes bits in column from being added to graph except target""" """Excludes bits in column from being added to graph except target"""
# Function is not robust with column mux configurations # Function is not robust with column mux configurations
@ -111,7 +112,7 @@ class bitcell_array(bitcell_base_array):
if row == targ_row and col == targ_col: if row == targ_row and col == targ_col:
continue continue
self.graph_inst_exclude.add(self.cell_inst[row, col]) self.graph_inst_exclude.add(self.cell_inst[row, col])
def get_cell_name(self, inst_name, row, col): def get_cell_name(self, inst_name, row, col):
"""Gets the spice name of the target bitcell.""" """Gets the spice name of the target bitcell."""
return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col] return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col]

View File

@ -8,14 +8,15 @@
import debug import debug
import design import design
from tech import cell_properties from tech import cell_properties
from sram_factory import factory
class bitcell_base_array(design.design): class bitcell_base_array(design.design):
""" """
Abstract base class for bitcell-arrays -- bitcell, dummy Abstract base class for bitcell-arrays -- bitcell, dummy, replica
""" """
def __init__(self, cols, rows, name, column_offset): def __init__(self, name, rows, cols, column_offset):
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols))
@ -23,56 +24,52 @@ class bitcell_base_array(design.design):
self.row_size = rows self.row_size = rows
self.column_offset = column_offset self.column_offset = column_offset
def get_all_bitline_names(self): # Bitcell for port names only
self.cell = factory.create(module_type="bitcell")
self.create_all_bitline_names()
self.create_all_wordline_names()
res = list() def get_all_bitline_names(self, prefix=""):
return [prefix + x for x in self.bitline_names]
def create_all_bitline_names(self):
self.bitline_names = list()
bitline_names = self.cell.get_all_bitline_names() bitline_names = self.cell.get_all_bitline_names()
# We have to keep the order of self.pins, otherwise we connect for col in range(self.column_size):
# it wrong in the spice netlist for cell_column in bitline_names:
for pin in self.pins: self.bitline_names.append("{0}_{1}".format(cell_column, col))
for bl_name in bitline_names:
if bl_name in pin:
res.append(pin)
return res
def get_all_wordline_names(self): def get_all_wordline_names(self, prefix=""):
return [prefix + x for x in self.wordline_names]
res = list() def create_all_wordline_names(self):
self.wordline_names = list()
wordline_names = self.cell.get_all_wl_names() wordline_names = self.cell.get_all_wl_names()
# We have to keep the order of self.pins, otherwise we connect for row in range(self.row_size):
# it wrong in the spice netlist for cell_row in wordline_names:
for pin in self.pins: self.wordline_names.append("{0}_{1}".format(cell_row, row))
for wl_name in wordline_names:
if wl_name in pin:
res.append(pin)
return res
def add_pins(self): def add_pins(self):
row_list = self.cell.get_all_wl_names() for bl_name in self.bitline_names:
column_list = self.cell.get_all_bitline_names() self.add_pin(bl_name, "INOUT")
for col in range(self.column_size): for wl_name in self.wordline_names:
for cell_column in column_list: self.add_pin(wl_name, "INPUT")
self.add_pin(cell_column+"_{0}".format(col), "INOUT")
for row in range(self.row_size):
for cell_row in row_list:
self.add_pin(cell_row+"_{0}".format(row), "INPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def get_bitcell_pins(self, col, row): def get_bitcell_pins(self, row, col):
""" Creates a list of connections in the bitcell, """ Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """ indexed by column and row, for instance use in bitcell_array """
bitcell_pins = [] bitcell_pins = []
# bitlines
pin_names = self.cell.get_all_bitline_names() bitcell_pins.extend([x for x in self.bitline_names if x.endswith("_{0}".format(col))])
for pin in pin_names: # wordlines
bitcell_pins.append(pin + "_{0}".format(col)) bitcell_pins.extend([x for x in self.wordline_names if x.endswith("_{0}".format(row))])
pin_names = self.cell.get_all_wl_names()
for pin in pin_names:
bitcell_pins.append(pin + "_{0}".format(row))
bitcell_pins.append("vdd") bitcell_pins.append("vdd")
bitcell_pins.append("gnd") bitcell_pins.append("gnd")
@ -81,22 +78,21 @@ class bitcell_base_array(design.design):
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
row_list = self.cell.get_all_wl_names() bitline_names = self.cell.get_all_bitline_names()
column_list = self.cell.get_all_bitline_names()
for col in range(self.column_size): for col in range(self.column_size):
for cell_column in column_list: for bl_name in bitline_names:
bl_pin = self.cell_inst[0, col].get_pin(cell_column) bl_pin = self.cell_inst[0, col].get_pin(bl_name)
self.add_layout_pin(text=cell_column + "_{0}".format(col), self.add_layout_pin(text="{0}_{1}".format(bl_name, col),
layer=bl_pin.layer, layer=bl_pin.layer,
offset=bl_pin.ll().scale(1, 0), offset=bl_pin.ll().scale(1, 0),
width=bl_pin.width(), width=bl_pin.width(),
height=self.height) height=self.height)
wl_names = self.cell.get_all_wl_names()
for row in range(self.row_size): for row in range(self.row_size):
for cell_row in row_list: for wl_name in wl_names:
wl_pin = self.cell_inst[row, 0].get_pin(cell_row) wl_pin = self.cell_inst[row, 0].get_pin(wl_name)
self.add_layout_pin(text=cell_row + "_{0}".format(row), self.add_layout_pin(text="{0}_{1}".format(wl_name, row),
layer=wl_pin.layer, layer=wl_pin.layer,
offset=wl_pin.ll().scale(0, 1), offset=wl_pin.ll().scale(0, 1),
width=self.width, width=self.width,

View File

@ -13,8 +13,8 @@ class col_cap_array(bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
super().__init__(cols, rows, name, column_offset) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror self.mirror = mirror
self.no_instances = True self.no_instances = True
@ -50,9 +50,9 @@ class col_cap_array(bitcell_base_array):
name = "bit_r{0}_c{1}".format(row, col) name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name, self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.dummy_cell) mod=self.dummy_cell)
self.connect_inst(self.get_bitcell_pins(col, row)) self.connect_inst(self.get_bitcell_pins(row, col))
def get_bitcell_pins(self, col, row): def get_bitcell_pins(self, row, col):
""" """
Creates a list of connections in the bitcell, Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array indexed by column and row, for instance use in bitcell_array

View File

@ -23,7 +23,7 @@ class control_logic(design.design):
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""): def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
""" Constructor """ """ Constructor """
name = "control_logic_" + port_type name = "control_logic_" + port_type
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {}".format(name)) debug.info(1, "Creating {}".format(name))
self.add_comment("num_rows: {0}".format(num_rows)) self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row)) self.add_comment("words_per_row: {0}".format(words_per_row))

View File

@ -21,7 +21,7 @@ class delay_chain(design.design):
def __init__(self, name, fanout_list): def __init__(self, name, fanout_list):
"""init function""" """init function"""
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "creating delay chain {0}".format(str(fanout_list))) debug.info(1, "creating delay chain {0}".format(str(fanout_list)))
self.add_comment("fanouts: {0}".format(str(fanout_list))) self.add_comment("fanouts: {0}".format(str(fanout_list)))

View File

@ -24,7 +24,7 @@ class dff_array(design.design):
if name=="": if name=="":
name = "dff_array_{0}x{1}".format(rows, columns) name = "dff_array_{0}x{1}".format(rows, columns)
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns)) debug.info(1, "Creating {0} rows={1} cols={2}".format(self.name, self.rows, self.columns))
self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns))

View File

@ -27,7 +27,7 @@ class dff_buf(design.design):
if name=="": if name=="":
name = "dff_buf_{0}".format(dff_buf.unique_id) name = "dff_buf_{0}".format(dff_buf.unique_id)
dff_buf.unique_id += 1 dff_buf.unique_id += 1
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))

View File

@ -27,7 +27,7 @@ class dff_buf_array(design.design):
if name=="": if name=="":
name = "dff_buf_array_{0}x{1}_{2}".format(rows, columns, dff_buf_array.unique_id) name = "dff_buf_array_{0}x{1}_{2}".format(rows, columns, dff_buf_array.unique_id)
dff_buf_array.unique_id += 1 dff_buf_array.unique_id += 1
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size)) self.add_comment("inv1: {0} inv2: {1}".format(inv1_size, inv2_size))

View File

@ -25,7 +25,7 @@ class dff_inv(design.design):
if name=="": if name=="":
name = "dff_inv_{0}".format(dff_inv.unique_id) name = "dff_inv_{0}".format(dff_inv.unique_id)
dff_inv.unique_id += 1 dff_inv.unique_id += 1
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.add_comment("inv: {0}".format(inv_size)) self.add_comment("inv: {0}".format(inv_size))

View File

@ -27,7 +27,7 @@ class dff_inv_array(design.design):
if name=="": if name=="":
name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id) name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id)
dff_inv_array.unique_id += 1 dff_inv_array.unique_id += 1
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {}".format(self.name)) debug.info(1, "Creating {}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, columns)) self.add_comment("rows: {0} cols: {1}".format(rows, columns))
self.add_comment("inv1: {0}".format(inv1_size)) self.add_comment("inv1: {0}".format(inv1_size))

View File

@ -12,8 +12,8 @@ class dummy_array(bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
super().__init__(cols, rows, name, column_offset) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror self.mirror = mirror
self.create_netlist() self.create_netlist()
@ -51,7 +51,7 @@ class dummy_array(bitcell_base_array):
name = "bit_r{0}_c{1}".format(row, col) name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name, self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.dummy_cell) mod=self.dummy_cell)
self.connect_inst(self.get_bitcell_pins(col, row)) self.connect_inst(self.get_bitcell_pins(row, col))
def input_load(self): def input_load(self):
wl_wire = self.gen_wl_wire() wl_wire = self.gen_wl_wire()

View File

@ -0,0 +1,66 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from bitcell_base_array import bitcell_base_array
from tech import drc, spice
from globals import OPTS
from sram_factory import factory
import debug
class global_bitcell_array(bitcell_base_array):
"""
Creates a global bitcell array with a number
of local arrays of a sizes given by a tuple in the list.
"""
def __init__(self, sizes, name=""):
# Each bank will have the same number of rows
self.rows = sizes[0][0]
for (r, c) in sizes:
debug.check(r[0] == self.rows, "Cannot have non-uniform number of rows in global array.")
# The total of all columns will be the number of columns
self.cols = sum(x[1] for x in sizes)
self.sizes = sizes
super().__init__(rows=self.rows, cols=self.cols, name=name)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
""" Create and connect the netlist """
self.add_modules()
self.add_pins()
self.create_instances()
def create_layout(self):
self.place()
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
self.local_mods = []
for (row, col) in self.sizes:
la = factory.create(module_type="local_bitcell_array", rows=row, cols=col)
self.add_mod(la)
self.local_mods.append(la)
def create_instances(self):
""" Create the module instances used in this design """
self.local_inst = {}
for i in range(self.sizes):
name = "local_array_{0}".format(i)
self.local_inst.append(self.add_inst(name=name,
mod=self.local_mods[i])
self.connect_inst(self.get_bitcell_pins(row, col))

View File

@ -18,7 +18,7 @@ class hierarchical_decoder(design.design):
Dynamically generated hierarchical decoder. Dynamically generated hierarchical decoder.
""" """
def __init__(self, name, num_outputs): def __init__(self, name, num_outputs):
design.design.__init__(self, name) super().__init__(name)
self.AND_FORMAT = "DEC_AND_{0}" self.AND_FORMAT = "DEC_AND_{0}"

View File

@ -31,7 +31,7 @@ class hierarchical_predecode(design.design):
self.column_decoder = (height != b.height) self.column_decoder = (height != b.height)
self.number_of_outputs = int(math.pow(2, self.number_of_inputs)) self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
design.design.__init__(self, name) super().__init__(name)
def add_pins(self): def add_pins(self):
for k in range(self.number_of_inputs): for k in range(self.number_of_inputs):

View File

@ -14,7 +14,7 @@ class hierarchical_predecode2x4(hierarchical_predecode):
Pre 2x4 decoder used in hierarchical_decoder. Pre 2x4 decoder used in hierarchical_decoder.
""" """
def __init__(self, name, height=None): def __init__(self, name, height=None):
hierarchical_predecode.__init__(self, name, 2, height) super().__init__( name, 2, height)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:

View File

@ -14,7 +14,7 @@ class hierarchical_predecode3x8(hierarchical_predecode):
Pre 3x8 decoder used in hierarchical_decoder. Pre 3x8 decoder used in hierarchical_decoder.
""" """
def __init__(self, name, height=None): def __init__(self, name, height=None):
hierarchical_predecode.__init__(self, name, 3, height) super().__init__(name, 3, height)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:

View File

@ -14,7 +14,7 @@ class hierarchical_predecode4x16(hierarchical_predecode):
Pre 4x16 decoder used in hierarchical_decoder. Pre 4x16 decoder used in hierarchical_decoder.
""" """
def __init__(self, name, height=None): def __init__(self, name, height=None):
hierarchical_predecode.__init__(self, name, 4, height) super().__init__(name, 4, height)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:

View File

@ -0,0 +1,168 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import bitcell_base_array
from globals import OPTS
from sram_factory import factory
from vector import vector
from tech import drc
import debug
class local_bitcell_array(bitcell_base_array.bitcell_base_array):
"""
A local bitcell array is a bitcell array with a wordline driver.
This can either be a single aray on its own if there is no hierarchical WL
or it can be combined into a larger array with hierarchical WL.
"""
def __init__(self, rows, cols, ports, left_rbl=0, right_rbl=0, add_replica=True, name=""):
super().__init__(name, rows, cols, 0)
debug.info(2, "create local array of size {} rows x {} cols words".format(rows,
cols + left_rbl + right_rbl))
self.rows = rows
self.cols = cols
self.add_replica=add_replica
self.all_ports = ports
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()
self.add_layout_pins()
self.route()
self.add_boundary()
self.DRC_LVS()
def add_modules(self):
""" Add the modules used in this design """
# This is just used for names
self.cell = factory.create(module_type="bitcell")
self.bitcell_array = factory.create(module_type="replica_bitcell_array",
cols=self.cols,
rows=self.rows,
left_rbl=1,
right_rbl=1 if len(self.all_ports)>1 else 0,
bitcell_ports=self.all_ports,
add_replica=self.add_replica)
self.add_mod(self.bitcell_array)
self.wl_array = factory.create(module_type="wordline_buffer_array",
rows=self.rows + len(self.all_ports),
cols=self.cols)
self.add_mod(self.wl_array)
def add_pins(self):
self.bitline_names = self.bitcell_array.get_all_bitline_names()
self.add_pin_list(self.bitline_names, "INOUT")
self.driver_wordline_inputs = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")]
self.driver_wordline_outputs = [x + "i" for x in self.driver_wordline_inputs]
self.array_wordline_inputs = [x + "i" if not x.startswith("dummy") else "gnd" for x in self.bitcell_array.get_all_wordline_names()]
self.add_pin_list(self.wordline_names, "INPUT")
self.replica_names = self.bitcell_array.get_rbl_wordline_names()
self.add_pin_list(self.replica_names, "INPUT")
self.bitline_names = self.bitcell_array.get_inouts()
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_instances(self):
""" Create the module instances used in this design """
self.wl_inst = self.add_inst(name="wl_driver",
mod=self.wl_array)
self.connect_inst(self.driver_wordline_inputs + self.driver_wordline_outputs + ["vdd", "gnd"])
self.bitcell_array_inst = self.add_inst(name="array",
mod=self.bitcell_array,
offset=self.wl_inst.lr())
self.connect_inst(self.bitline_names + self.array_wordline_inputs + ["vdd", "gnd"])
def place(self):
""" Place the bitcelll array to the right of the wl driver. """
self.wl_inst.place(vector(0, self.cell.height))
# FIXME: Replace this with a tech specific paramter
driver_to_array_spacing = 3 * self.m3_pitch
self.bitcell_array_inst.place(vector(self.wl_inst.rx() + driver_to_array_spacing,
0))
self.height = self.bitcell_array.height
self.width = self.bitcell_array_inst.rx()
def route_unused_wordlines(self):
""" Connect the unused RBL and dummy wordlines to gnd """
gnd_wl_names = []
# Connect unused RBL WL to gnd
array_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("rbl")])
dummy_rbl_names = set([x for x in self.bitcell_array.get_all_wordline_names() if x.startswith("dummy")])
rbl_wl_names = set([self.bitcell_array.get_rbl_wordline_names(x) for x in self.all_ports])
gnd_wl_names = list((array_rbl_names - rbl_wl_names) | dummy_rbl_names)
for wl_name in gnd_wl_names:
pin = self.bitcell_array_inst.get_pin(wl_name)
pin_layer = pin.layer
layer_pitch = 1.5 * getattr(self, "{}_pitch".format(pin_layer))
left_pin_loc = pin.lc()
right_pin_loc = pin.rc()
# Place the pins a track outside of the array
left_loc = left_pin_loc - vector(layer_pitch, 0)
right_loc = right_pin_loc + vector(layer_pitch, 0)
self.add_power_pin("gnd", left_loc, directions=("H", "H"))
self.add_power_pin("gnd", right_loc, directions=("H", "H"))
# Add a path to connect to the array
self.add_path(pin_layer, [left_loc, left_pin_loc])
self.add_path(pin_layer, [right_loc, right_pin_loc])
def add_layout_pins(self):
for (x, y) in zip(self.bitline_names, self.bitcell_array.get_inouts()):
self.copy_layout_pin(self.bitcell_array_inst, y, x)
for (x, y) in zip(self.driver_wordline_inputs, self.wl_array.get_inputs()):
self.copy_layout_pin(self.wl_inst, y, x)
supply_insts = [self.wl_inst, self.bitcell_array_inst]
for pin_name in ["vdd", "gnd"]:
for inst in supply_insts:
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
self.add_power_pin(name=pin_name,
loc=pin.center(),
start_layer=pin.layer)
def route(self):
array_names = [x for x in self.bitcell_array.get_all_wordline_names() if not x.startswith("dummy")]
for (driver_name, array_name) in zip(self.wl_array.get_outputs(), array_names):
out_pin = self.wl_inst.get_pin(driver_name)
in_pin = self.bitcell_array_inst.get_pin(array_name)
mid_loc = self.wl_inst.rx() + 1.5 * self.m3_pitch
self.add_path(out_pin.layer, [out_pin.rc(), vector(mid_loc, out_pin.cy()), in_pin.lc()])
self.route_unused_wordlines()

View File

@ -26,7 +26,7 @@ class multibank(design.design):
def __init__(self, name, word_size, num_words, words_per_row, num_banks=1): def __init__(self, name, word_size, num_words, words_per_row, num_banks=1):
design.design.__init__(self, name) super().__init__(name)
debug.info(2, "create sram of size {0} with {1} words".format(word_size,num_words)) debug.info(2, "create sram of size {0} with {1} words".format(word_size,num_words))
self.word_size = word_size self.word_size = word_size

View File

@ -25,7 +25,7 @@ class port_address(design.design):
if name == "": if name == "":
name = "port_address_{0}_{1}".format(cols, rows) name = "port_address_{0}_{1}".format(cols, rows)
design.design.__init__(self, name) super().__init__(name)
debug.info(2, "create data port of cols {0} rows {1}".format(cols, rows)) debug.info(2, "create data port of cols {0} rows {1}".format(cols, rows))
self.create_netlist() self.create_netlist()

View File

@ -32,7 +32,7 @@ class port_data(design.design):
if name == "": if name == "":
name = "port_data_{0}".format(self.port) name = "port_data_{0}".format(self.port)
design.design.__init__(self, name) super().__init__(name)
debug.info(2, debug.info(2,
"create data port of size {0} with {1} words per row".format(self.word_size, "create data port of size {0} with {1} words per row".format(self.word_size,
self.words_per_row)) self.words_per_row))

View File

@ -20,7 +20,7 @@ class precharge_array(design.design):
""" """
def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0): def __init__(self, name, columns, size=1, bitcell_bl="bl", bitcell_br="br", column_offset=0):
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br)) self.add_comment("cols: {0} size: {1} bl: {2} br: {3}".format(columns, size, bitcell_bl, bitcell_br))

View File

@ -5,14 +5,14 @@
# #
import debug import debug
import design import bitcell_base_array
from tech import drc, spice, cell_properties from tech import drc, spice, cell_properties
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class replica_bitcell_array(design.design): class replica_bitcell_array(bitcell_base_array.bitcell_base_array):
""" """
Creates a bitcell arrow of cols x rows and then adds the replica Creates a bitcell arrow of cols x rows and then adds the replica
and dummy columns and rows. Replica columns are on the left and and dummy columns and rows. Replica columns are on the left and
@ -21,8 +21,8 @@ class replica_bitcell_array(design.design):
Requires a regular bitcell array, replica bitcell, and dummy Requires a regular bitcell array, replica bitcell, and dummy
bitcell (Bl/BR disconnected). bitcell (Bl/BR disconnected).
""" """
def __init__(self, cols, rows, left_rbl, right_rbl, bitcell_ports, name): def __init__(self, rows, cols, left_rbl, right_rbl, bitcell_ports, name, add_replica=True):
design.design.__init__(self, name) super().__init__(name, rows, cols, column_offset=0)
debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols)) debug.info(1, "Creating {0} {1} x {2}".format(self.name, rows, cols))
self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols))
@ -31,15 +31,26 @@ class replica_bitcell_array(design.design):
self.left_rbl = left_rbl self.left_rbl = left_rbl
self.right_rbl = right_rbl self.right_rbl = right_rbl
self.bitcell_ports = bitcell_ports self.bitcell_ports = bitcell_ports
# If set to false, we increase the height for the replica wordline row, but don't
# actually add the column to this array. This is so the height matches other
# banks that have the replica columns.
# Number of replica columns to actually add
if add_replica:
self.add_left_rbl = self.left_rbl
self.add_right_rbl = self.right_rbl
else:
self.add_left_rbl = 0
self.add_right_rbl = 0
debug.check(left_rbl + right_rbl == len(self.all_ports), debug.check(left_rbl + right_rbl <= len(self.all_ports),
"Invalid number of RBLs for port configuration.") "Invalid number of RBLs for port configuration.")
debug.check(left_rbl + right_rbl == len(self.bitcell_ports), debug.check(left_rbl + right_rbl <= len(self.bitcell_ports),
"Bitcell ports must match total RBLs.") "Bitcell ports must match total RBLs.")
# Two dummy rows/cols plus replica for each port # Two dummy rows plus replica even if we don't add the column
self.extra_rows = 2 + left_rbl + right_rbl self.extra_rows = 2 + self.left_rbl + self.right_rbl
self.extra_cols = 2 + left_rbl + right_rbl # Two dummy cols plus replica if we add the column
self.extra_cols = 2 + self.add_left_rbl + self.add_right_rbl
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -79,33 +90,30 @@ class replica_bitcell_array(design.design):
1 x (rows + 4) 1 x (rows + 4)
""" """
# Bitcell for port names only
self.cell = factory.create(module_type="bitcell")
# Bitcell array # Bitcell array
self.bitcell_array = factory.create(module_type="bitcell_array", self.bitcell_array = factory.create(module_type="bitcell_array",
column_offset=1 + self.left_rbl, column_offset=1 + self.add_left_rbl,
cols=self.column_size, cols=self.column_size,
rows=self.row_size) rows=self.row_size)
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
# Replica bitlines # Replica bitlines
self.replica_columns = {} self.replica_columns = {}
for bit in range(self.left_rbl + self.right_rbl): for bit in range(self.add_left_rbl + self.add_right_rbl):
# Creating left_rbl # Creating left_rbl
if bit<self.left_rbl: if bit < self.add_left_rbl:
replica_bit = bit + 1 # These go from the top (where the bitcell array starts ) down
# dummy column replica_bit = self.left_rbl - bit
column_offset = self.left_rbl - bit
# Creating right_rbl # Creating right_rbl
else: else:
replica_bit = bit + self.row_size + 1 # These go from the bottom up
# dummy column + replica column + bitcell colums replica_bit = self.left_rbl + self.row_size + 1 + bit
column_offset = self.left_rbl - bit + self.row_size # If we have an odd numer on the bottom
column_offset = self.left_rbl + 1
self.replica_columns[bit] = factory.create(module_type="replica_column", self.replica_columns[bit] = factory.create(module_type="replica_column",
rows=self.row_size, rows=self.row_size,
left_rbl=self.left_rbl, left_rbl=self.add_left_rbl,
right_rbl=self.right_rbl, right_rbl=self.add_right_rbl,
column_offset=column_offset, column_offset=column_offset,
replica_bit=replica_bit) replica_bit=replica_bit)
self.add_mod(self.replica_columns[bit]) self.add_mod(self.replica_columns[bit])
@ -115,7 +123,7 @@ class replica_bitcell_array(design.design):
cols=self.column_size, cols=self.column_size,
rows=1, rows=1,
# dummy column + left replica column # dummy column + left replica column
column_offset=1 + self.left_rbl, column_offset=1 + self.add_left_rbl,
mirror=0) mirror=0)
self.add_mod(self.dummy_row) self.add_mod(self.dummy_row)
@ -126,108 +134,134 @@ class replica_bitcell_array(design.design):
end_caps_enabled = False end_caps_enabled = False
# Dummy Row or Col Cap, depending on bitcell array properties # Dummy Row or Col Cap, depending on bitcell array properties
edge_row_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array") col_cap_module_type = ("col_cap_array" if end_caps_enabled else "dummy_array")
self.col_cap = factory.create(module_type=col_cap_module_type,
self.edge_row = factory.create(module_type=edge_row_module_type,
cols=self.column_size, cols=self.column_size,
rows=1, rows=1,
# dummy column + left replica column(s) # dummy column + left replica column(s)
column_offset=1 + self.left_rbl, column_offset=1 + self.add_left_rbl,
mirror=0) mirror=0)
self.add_mod(self.edge_row) self.add_mod(self.col_cap)
# Dummy Col or Row Cap, depending on bitcell array properties # Dummy Col or Row Cap, depending on bitcell array properties
edge_col_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array") row_cap_module_type = ("row_cap_array" if end_caps_enabled else "dummy_array")
self.edge_col_left = factory.create(module_type=edge_col_module_type, self.row_cap_left = factory.create(module_type=row_cap_module_type,
cols=1, cols=1,
column_offset=0, column_offset=0,
rows=self.row_size + self.extra_rows, rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl + 1) % 2) mirror=(self.left_rbl + 1) % 2)
self.add_mod(self.edge_col_left) self.add_mod(self.row_cap_left)
self.edge_col_right = factory.create(module_type=edge_col_module_type, self.row_cap_right = factory.create(module_type=row_cap_module_type,
cols=1, cols=1,
# dummy column # dummy column
# + left replica column(s) # + left replica column(s)
# + bitcell columns # + bitcell columns
# + right replica column(s) # + right replica column(s)
column_offset = 1 + self.left_rbl + self.column_size + self.right_rbl, column_offset = 1 + self.add_left_rbl + self.column_size + self.add_right_rbl,
rows=self.row_size + self.extra_rows, rows=self.row_size + self.extra_rows,
mirror=(self.left_rbl + 1) %2) mirror=(self.left_rbl + 1) %2)
self.add_mod(self.edge_col_right) self.add_mod(self.row_cap_right)
def add_pins(self): def add_pins(self):
self.bitcell_array_wl_names = self.bitcell_array.get_all_wordline_names()
self.bitcell_array_bl_names = self.bitcell_array.get_all_bitline_names()
# These are the non-indexed names self.add_bitline_pins()
self.dummy_cell_wl_names = ["dummy_" + x for x in self.cell.get_all_wl_names()] self.add_wordline_pins()
self.dummy_cell_bl_names = ["dummy_" + x for x in self.cell.get_all_bitline_names()]
self.dummy_row_bl_names = self.bitcell_array_bl_names
# A dictionary because some ports may have nothing
self.rbl_bl_names = {}
self.rbl_br_names = {}
self.rbl_wl_names = {}
# Create the full WL names include dummy, replica, and regular bit cells
self.replica_col_wl_names = []
self.replica_col_wl_names.extend(["{0}_bot".format(x) for x in self.dummy_cell_wl_names])
# Left port WLs (one dummy for each port when we allow >1 port)
for port in range(self.left_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))]
# Keep track of the pin that is the RBL
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
self.replica_col_wl_names.extend(wl_names)
# Regular WLs
self.replica_col_wl_names.extend(self.bitcell_array_wl_names)
# Right port WLs (one dummy for each port when we allow >1 port)
for port in range(self.left_rbl, self.left_rbl + self.right_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(self.cell.get_wl_name(x), port) for x in range(len(self.cell.get_all_wl_names()))]
# Keep track of the pin that is the RBL
self.rbl_wl_names[port]=wl_names[self.bitcell_ports[port]]
self.replica_col_wl_names.extend(wl_names)
self.replica_col_wl_names.extend(["{0}_top".format(x) for x in self.dummy_cell_wl_names])
# Left/right dummy columns are connected identically to the replica column
self.dummy_col_wl_names = self.replica_col_wl_names
# Per port bitline names
self.replica_bl_names = {}
self.replica_wl_names = {}
# Array of all port bitline names
for port in range(self.left_rbl + self.right_rbl):
left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))]
right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))]
# Keep track of the left pins that are the RBL
self.rbl_bl_names[port]=left_names[self.bitcell_ports[port]]
self.rbl_br_names[port]=right_names[self.bitcell_ports[port]]
# Interleave the left and right lists
bl_names = [x for t in zip(left_names, right_names) for x in t]
self.replica_bl_names[port] = bl_names
wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
self.replica_wl_names[port] = wl_names
# External pins
self.add_pin_list(self.bitcell_array_bl_names, "INOUT")
# Need to sort by port order since dictionary values may not be in order
bl_names = [self.rbl_bl_names[x] for x in sorted(self.rbl_bl_names.keys())]
br_names = [self.rbl_br_names[x] for x in sorted(self.rbl_br_names.keys())]
for (bl_name, br_name) in zip(bl_names, br_names):
self.add_pin(bl_name, "OUTPUT")
self.add_pin(br_name, "OUTPUT")
self.add_pin_list(self.bitcell_array_wl_names, "INPUT")
# Need to sort by port order since dictionary values may not be in order
wl_names = [self.rbl_wl_names[x] for x in sorted(self.rbl_wl_names.keys())]
for pin_name in wl_names:
self.add_pin(pin_name, "INPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_bitline_pins(self):
# Regular bitline names for all ports
self.bitline_names = []
# Bitline names for each port
self.bitline_names_by_port = [[] for x in self.all_ports]
# Replica wordlines by port
self.replica_bitline_names = [[] for x in self.all_ports]
# Replica wordlines by port (bl only)
self.replica_bl_names = [[] for x in self.all_ports]
# Dummy wordlines by port
self.dummy_bitline_names = []
# Regular array bitline names
self.bitcell_array_bitline_names = self.bitcell_array.get_all_bitline_names()
# These are the non-indexed names
dummy_bitline_names = ["dummy_" + x for x in self.cell.get_all_bitline_names()]
self.dummy_bitline_names.append([x + "_left" for x in dummy_bitline_names])
self.dummy_bitline_names.append([x + "_right" for x in dummy_bitline_names])
# Array of all port bitline names
for port in range(self.add_left_rbl + self.add_right_rbl):
left_names=["rbl_{0}_{1}".format(self.cell.get_bl_name(x), port) for x in range(len(self.all_ports))]
right_names=["rbl_{0}_{1}".format(self.cell.get_br_name(x), port) for x in range(len(self.all_ports))]
# Keep track of the left pins that are the RBL
self.replica_bl_names[port]=left_names[self.bitcell_ports[port]]
# Interleave the left and right lists
bitline_names = [x for t in zip(left_names, right_names) for x in t]
self.replica_bitline_names[port] = bitline_names
# Dummy bitlines are not connected to anything
self.bitline_names.extend(self.bitcell_array_bitline_names)
for port in self.all_ports:
self.add_pin_list(self.replica_bitline_names[port], "INOUT")
self.add_pin_list(self.bitline_names, "INOUT")
def add_wordline_pins(self):
# All wordline names for all ports
self.wordline_names = []
# Wordline names for each port
self.wordline_names_by_port = [[] for x in self.all_ports]
# Replica wordlines by port
self.replica_wordline_names = [[] for x in self.all_ports]
# Dummy wordlines
self.dummy_wordline_names = {}
# Regular array wordline names
self.bitcell_array_wordline_names = self.bitcell_array.get_all_wordline_names()
# These are the non-indexed names
dummy_cell_wl_names = ["dummy_" + x for x in self.cell.get_all_wl_names()]
# Create the full WL names include dummy, replica, and regular bit cells
self.wordline_names = []
self.dummy_wordline_names["bot"] = ["{0}_bot".format(x) for x in dummy_cell_wl_names]
self.wordline_names.extend(self.dummy_wordline_names["bot"])
# Left port WLs
for port in range(self.left_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
# Keep track of the pin that is the RBL
self.replica_wordline_names[port] = wl_names
self.wordline_names.extend(wl_names)
# Regular WLs
self.wordline_names.extend(self.bitcell_array_wordline_names)
# Right port WLs
for port in range(self.left_rbl, self.left_rbl + self.right_rbl):
# Make names for all RBLs
wl_names=["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
# Keep track of the pin that is the RBL
self.replica_wordline_names[port] = wl_names
self.wordline_names.extend(wl_names)
self.dummy_wordline_names["top"] = ["{0}_top".format(x) for x in dummy_cell_wl_names]
self.wordline_names.extend(self.dummy_wordline_names["top"])
# Array of all port wl names
for port in range(self.left_rbl + self.right_rbl):
wl_names = ["rbl_{0}_{1}".format(x, port) for x in self.cell.get_all_wl_names()]
self.replica_wordline_names[port] = wl_names
self.add_pin_list(self.wordline_names, "INPUT")
def create_instances(self): def create_instances(self):
""" Create the module instances used in this design """ """ Create the module instances used in this design """
@ -239,37 +273,42 @@ class replica_bitcell_array(design.design):
# Main array # Main array
self.bitcell_array_inst=self.add_inst(name="bitcell_array", self.bitcell_array_inst=self.add_inst(name="bitcell_array",
mod=self.bitcell_array) mod=self.bitcell_array)
self.connect_inst(self.bitcell_array_bl_names + self.bitcell_array_wl_names + supplies) self.connect_inst(self.bitcell_array_bitline_names + self.bitcell_array_wordline_names + supplies)
# Replica columns # Replica columns
self.replica_col_inst = {} self.replica_col_inst = {}
for port in range(self.left_rbl + self.right_rbl): for port in range(self.add_left_rbl + self.add_right_rbl):
self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port), self.replica_col_inst[port]=self.add_inst(name="replica_col_{}".format(port),
mod=self.replica_columns[port]) mod=self.replica_columns[port])
self.connect_inst(self.replica_bl_names[port] + self.replica_col_wl_names + supplies) self.connect_inst(self.replica_bitline_names[port] + self.wordline_names + supplies)
# Dummy rows under the bitcell array (connected with with the replica cell wl) # Dummy rows under the bitcell array (connected with with the replica cell wl)
self.dummy_row_replica_inst = {} self.dummy_row_replica_inst = {}
# Note, this is the number of left and right even if we aren't adding the columns to this bitcell array!
for port in range(self.left_rbl + self.right_rbl): for port in range(self.left_rbl + self.right_rbl):
self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port), self.dummy_row_replica_inst[port]=self.add_inst(name="dummy_row_{}".format(port),
mod=self.dummy_row) mod=self.dummy_row)
self.connect_inst(self.dummy_row_bl_names + self.replica_wl_names[port] + supplies) self.connect_inst(self.bitcell_array_bitline_names + self.replica_wordline_names[port] + supplies)
# Top/bottom dummy rows or col caps # Top/bottom dummy rows or col caps
self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot", self.dummy_row_bot_inst=self.add_inst(name="dummy_row_bot",
mod=self.edge_row) mod=self.col_cap)
self.connect_inst(self.dummy_row_bl_names + [x + "_bot" for x in self.dummy_cell_wl_names] + supplies) self.connect_inst(self.bitcell_array_bitline_names
+ self.dummy_wordline_names["bot"]
+ supplies)
self.dummy_row_top_inst=self.add_inst(name="dummy_row_top", self.dummy_row_top_inst=self.add_inst(name="dummy_row_top",
mod=self.edge_row) mod=self.col_cap)
self.connect_inst(self.dummy_row_bl_names + [x + "_top" for x in self.dummy_cell_wl_names] + supplies) self.connect_inst(self.bitcell_array_bitline_names
+ self.dummy_wordline_names["top"]
+ supplies)
# Left/right Dummy columns # Left/right Dummy columns
self.dummy_col_left_inst=self.add_inst(name="dummy_col_left", self.dummy_col_left_inst=self.add_inst(name="dummy_col_left",
mod=self.edge_col_left) mod=self.row_cap_left)
self.connect_inst([x + "_left" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) self.connect_inst(self.dummy_bitline_names[0] + self.wordline_names + supplies)
self.dummy_col_right_inst=self.add_inst(name="dummy_col_right", self.dummy_col_right_inst=self.add_inst(name="dummy_col_right",
mod=self.edge_col_right) mod=self.row_cap_right)
self.connect_inst([x + "_right" for x in self.dummy_cell_bl_names] + self.dummy_col_wl_names + supplies) self.connect_inst(self.dummy_bitline_names[-1] + self.wordline_names + supplies)
def create_layout(self): def create_layout(self):
@ -277,41 +316,18 @@ class replica_bitcell_array(design.design):
self.width = (self.column_size + self.extra_cols) * self.cell.width self.width = (self.column_size + self.extra_cols) * self.cell.width
# This is a bitcell x bitcell offset to scale # This is a bitcell x bitcell offset to scale
offset = vector(self.cell.width, self.cell.height) self.bitcell_offset = vector(self.cell.width, self.cell.height)
# Everything is computed with the main array at (0, 0) to start
self.bitcell_array_inst.place(offset=[0, 0]) self.bitcell_array_inst.place(offset=[0, 0])
# To the left of the bitcell array self.add_replica_columns()
for bit in range(self.left_rbl):
self.replica_col_inst[bit].place(offset=offset.scale(-bit - 1, -self.left_rbl - 1)) self.add_end_caps()
# To the right of the bitcell array
for bit in range(self.right_rbl):
self.replica_col_inst[self.left_rbl + bit].place(offset=offset.scale(bit, -self.left_rbl - 1) + self.bitcell_array_inst.lr())
# FIXME: These depend on the array size itself # Array was at (0, 0) but move everything so it is at the lower left
# Far top dummy row (first row above array is NOT flipped) # We move DOWN the number of left RBL even if we didn't add the column to this bitcell array
flip_dummy = self.right_rbl % 2 self.translate_all(self.bitcell_offset.scale(-1 - self.add_left_rbl, -1 - self.left_rbl))
self.dummy_row_top_inst.place(offset=offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul(),
mirror="MX" if flip_dummy else "R0")
# FIXME: These depend on the array size itself
# Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.left_rbl + 1) % 2
self.dummy_row_bot_inst.place(offset=offset.scale(0, -self.left_rbl - 1 + flip_dummy),
mirror="MX" if flip_dummy else "R0")
# Far left dummy col
self.dummy_col_left_inst.place(offset=offset.scale(-self.left_rbl - 1, -self.left_rbl - 1))
# Far right dummy col
self.dummy_col_right_inst.place(offset=offset.scale(self.right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr())
# Replica dummy rows
for bit in range(self.left_rbl):
self.dummy_row_replica_inst[bit].place(offset=offset.scale(0, -bit - bit % 2),
mirror="R0" if bit % 2 else "MX")
for bit in range(self.right_rbl):
self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(),
mirror="MX" if bit % 2 else "R0")
self.translate_all(offset.scale(-1 - self.left_rbl, -1 - self.left_rbl))
self.add_layout_pins() self.add_layout_pins()
@ -319,22 +335,70 @@ class replica_bitcell_array(design.design):
self.DRC_LVS() self.DRC_LVS()
def add_replica_columns(self):
""" Add replica columns on left and right of array """
# Grow from left to right, toward the array
for bit in range(self.add_left_rbl):
offset = self.bitcell_offset.scale(-self.add_left_rbl + bit, -self.add_left_rbl - 1)
self.replica_col_inst[bit].place(offset)
# Grow to the right of the bitcell array, array outward
for bit in range(self.add_right_rbl):
offset = self.bitcell_array_inst.lr() + self.bitcell_offset.scale(bit, -self.add_left_rbl - 1)
self.replica_col_inst[self.add_left_rbl + 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.left_rbl):
self.dummy_row_replica_inst[bit].place(offset=self.bitcell_offset.scale(0, -self.left_rbl + bit + (-self.left_rbl + bit) % 2),
mirror="MX" if (-self.left_rbl + bit) % 2 else "R0")
# These grow up, away from the array
for bit in range(self.right_rbl):
self.dummy_row_replica_inst[self.left_rbl + bit].place(offset=self.bitcell_offset.scale(0, bit + bit % 2) + self.bitcell_array_inst.ul(),
mirror="MX" if bit % 2 else "R0")
def add_end_caps(self):
""" Add dummy cells or end caps around the array """
# FIXME: These depend on the array size itself
# Far top dummy row (first row above array is NOT flipped)
flip_dummy = self.right_rbl % 2
dummy_row_offset = self.bitcell_offset.scale(0, self.right_rbl + flip_dummy) + self.bitcell_array_inst.ul()
self.dummy_row_top_inst.place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# FIXME: These depend on the array size itself
# Far bottom dummy row (first row below array IS flipped)
flip_dummy = (self.left_rbl + 1) % 2
dummy_row_offset = self.bitcell_offset.scale(0, -self.left_rbl - 1 + flip_dummy)
self.dummy_row_bot_inst.place(offset=dummy_row_offset,
mirror="MX" if flip_dummy else "R0")
# 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(-self.add_left_rbl - 1, -self.left_rbl - 1)
self.dummy_col_left_inst.place(offset=dummy_col_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(self.add_right_rbl, -self.left_rbl - 1) + self.bitcell_array_inst.lr()
self.dummy_col_right_inst.place(offset=dummy_col_offset)
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """
# All wordlines
# Main array wl and bl/br # Main array wl and bl/br
pin_names = self.bitcell_array.get_pin_names() pin_names = self.bitcell_array.get_pin_names()
for pin_name in pin_names: for pin_name in pin_names:
for wl in self.bitcell_array_wl_names: for wl in self.bitcell_array_wordline_names:
if wl in pin_name: if wl in pin_name:
pin_list = self.bitcell_array_inst.get_pins(pin_name) pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list: for pin in pin_list:
self.add_layout_pin(text=pin_name, self.add_layout_pin(text=pin_name,
layer=pin.layer, layer=pin.layer,
offset=pin.ll().scale(0, 1), offset=pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=pin.height()) height=pin.height())
for bitline in self.bitcell_array_bl_names: for bitline in self.bitcell_array_bitline_names:
if bitline in pin_name: if bitline in pin_name:
pin_list = self.bitcell_array_inst.get_pins(pin_name) pin_list = self.bitcell_array_inst.get_pins(pin_name)
for pin in pin_list: for pin in pin_list:
@ -344,31 +408,35 @@ class replica_bitcell_array(design.design):
width=pin.width(), width=pin.width(),
height=self.height) height=self.height)
# Replica wordlines # Dummy wordlines
for port in range(self.left_rbl + self.right_rbl): for (name, inst) in [("bot", self.dummy_row_bot_inst), ("top", self.dummy_row_top_inst)]:
inst = self.replica_col_inst[port] for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.dummy_wordline_names[name]):
for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wl_names[port]): # It's always a single row
# +1 for dummy row pin = inst.get_pin(pin_name + "_0")
pin_bit = port + 1 self.add_layout_pin(text=wl_name,
# +row_size if above the array layer=pin.layer,
if port>=self.left_rbl: offset=pin.ll().scale(0, 1),
pin_bit += self.row_size width=self.width,
height=pin.height())
pin_name += "_{}".format(pin_bit) # Replica wordlines (go by the row instead of replica column because we may have to add a pin
pin = inst.get_pin(pin_name) # even though the column is in another local bitcell array)
if wl_name in self.rbl_wl_names.values(): for (port, inst) in list(self.dummy_row_replica_inst.items()):
self.add_layout_pin(text=wl_name, for (pin_name, wl_name) in zip(self.cell.get_all_wl_names(), self.replica_wordline_names[port]):
layer=pin.layer, pin = inst.get_pin(pin_name + "_0")
offset=pin.ll().scale(0, 1), self.add_layout_pin(text=wl_name,
width=self.width, layer=pin.layer,
height=pin.height()) offset=pin.ll().scale(0, 1),
width=self.width,
height=pin.height())
# Replica bitlines # Replica bitlines
for port in range(self.left_rbl + self.right_rbl): for port in range(self.add_left_rbl + self.add_right_rbl):
inst = self.replica_col_inst[port] inst = self.replica_col_inst[port]
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bl_names[port]): for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bitline_names[port]):
pin = inst.get_pin(pin_name) pin = inst.get_pin(pin_name)
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
if bl_name in self.replica_bl_names:
name = bl_name name = bl_name
else: else:
name = "rbl_{0}_{1}".format(pin_name, port) name = "rbl_{0}_{1}".format(pin_name, port)
@ -390,22 +458,42 @@ class replica_bitcell_array(design.design):
loc=pin.center(), loc=pin.center(),
directions=("V", "V"), directions=("V", "V"),
start_layer=pin.layer) start_layer=pin.layer)
for inst in list(self.replica_col_inst.values()): for inst in list(self.replica_col_inst.values()):
self.copy_layout_pin(inst, pin_name) self.copy_layout_pin(inst, pin_name)
self.copy_layout_pin(inst, pin_name)
def get_rbl_wl_name(self, port): def get_rbl_wordline_names(self, port=None):
""" Return the WL for the given RBL port """ """
return self.rbl_wl_names[port] Return the ACTIVE WL for the given RBL port.
Inactive will be set to gnd.
"""
if port == None:
temp = []
for port in self.all_ports:
temp.extend(self.replica_wordline_names[port])
return temp
else:
wl_names = self.replica_wordline_names[port]
return wl_names[port]
def get_rbl_bl_name(self, port): def get_rbl_bitline_names(self, port=None):
""" Return the BL for the given RBL port """ """ Return the BL for the given RBL port """
return self.rbl_bl_names[port] if port == None:
temp = []
for port in self.all_ports:
temp.extend(self.replica_bitline_names[port])
return temp
else:
bl_names = self.replica_bitline_names[port]
return bl_names[2 * port:2 * port + 2]
def get_rbl_br_name(self, port): def get_wordline_names(self):
""" Return the BR for the given RBL port """ """ Return the wordline names """
return self.rbl_br_names[port] return self.wordline_names
def get_bitline_names(self):
""" Return the bitline names """
return self.bitline_names
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Power of Bitcell array and bitline in nW.""" """Power of Bitcell array and bitline in nW."""

View File

@ -22,7 +22,7 @@ class replica_column(design.design):
def __init__(self, name, rows, left_rbl, right_rbl, replica_bit, def __init__(self, name, rows, left_rbl, right_rbl, replica_bit,
column_offset=0): column_offset=0):
design.design.__init__(self, name) super().__init__(name)
self.rows = rows self.rows = rows
self.left_rbl = left_rbl self.left_rbl = left_rbl
@ -102,22 +102,22 @@ class replica_column(design.design):
if (row > self.left_rbl and row < self.total_size - self.right_rbl - 1): if (row > self.left_rbl and row < self.total_size - self.right_rbl - 1):
self.cell_inst[row]=self.add_inst(name=name, self.cell_inst[row]=self.add_inst(name=name,
mod=self.replica_cell) mod=self.replica_cell)
self.connect_inst(self.get_bitcell_pins(0, row)) self.connect_inst(self.get_bitcell_pins(row, 0))
elif row==self.replica_bit: elif row==self.replica_bit:
self.cell_inst[row]=self.add_inst(name=name, self.cell_inst[row]=self.add_inst(name=name,
mod=self.replica_cell) mod=self.replica_cell)
self.connect_inst(self.get_bitcell_pins(0, row)) self.connect_inst(self.get_bitcell_pins(row, 0))
elif (row == 0 or row == self.total_size - 1): elif (row == 0 or row == self.total_size - 1):
self.cell_inst[row]=self.add_inst(name=name, self.cell_inst[row]=self.add_inst(name=name,
mod=self.edge_cell) mod=self.edge_cell)
if end_caps_enabled: if end_caps_enabled:
self.connect_inst(self.get_bitcell_pins_col_cap(0, row)) self.connect_inst(self.get_bitcell_pins_col_cap(row, 0))
else: else:
self.connect_inst(self.get_bitcell_pins(0, row)) self.connect_inst(self.get_bitcell_pins(row, 0))
else: else:
self.cell_inst[row]=self.add_inst(name=name, self.cell_inst[row]=self.add_inst(name=name,
mod=self.dummy_cell) mod=self.dummy_cell)
self.connect_inst(self.get_bitcell_pins(0, row)) self.connect_inst(self.get_bitcell_pins(row, 0))
def place_instances(self): def place_instances(self):
from tech import cell_properties from tech import cell_properties
@ -191,7 +191,7 @@ class replica_column(design.design):
else: else:
self.copy_layout_pin(inst, pin_name) self.copy_layout_pin(inst, pin_name)
def get_bitcell_pins(self, col, row): def get_bitcell_pins(self, row, col):
""" Creates a list of connections in the bitcell, """ Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """ indexed by column and row, for instance use in bitcell_array """
@ -208,7 +208,7 @@ class replica_column(design.design):
return bitcell_pins return bitcell_pins
def get_bitcell_pins_col_cap(self, col, row): def get_bitcell_pins_col_cap(self, row, col):
""" Creates a list of connections in the bitcell, """ Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array """ indexed by column and row, for instance use in bitcell_array """

View File

@ -13,8 +13,8 @@ class row_cap_array(bitcell_base_array):
""" """
Generate a dummy row/column for the replica array. Generate a dummy row/column for the replica array.
""" """
def __init__(self, cols, rows, column_offset=0, mirror=0, name=""): def __init__(self, rows, cols, column_offset=0, mirror=0, name=""):
super().__init__(cols, rows, name, column_offset) super().__init__(rows=rows, cols=cols, column_offset=column_offset, name=name)
self.mirror = mirror self.mirror = mirror
self.no_instances = True self.no_instances = True
self.create_netlist() self.create_netlist()
@ -49,9 +49,9 @@ class row_cap_array(bitcell_base_array):
name = "bit_r{0}_c{1}".format(row, col) name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row, col]=self.add_inst(name=name, self.cell_inst[row, col]=self.add_inst(name=name,
mod=self.dummy_cell) mod=self.dummy_cell)
self.connect_inst(self.get_bitcell_pins(col, row)) self.connect_inst(self.get_bitcell_pins(row, col))
def get_bitcell_pins(self, col, row): def get_bitcell_pins(self, row, col):
""" """
Creates a list of connections in the bitcell, Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array indexed by column and row, for instance use in bitcell_array

View File

@ -50,7 +50,7 @@ class sense_amp(design.design):
return props.sense_amp.pin.en return props.sense_amp.pin.en
def __init__(self, name): def __init__(self, name):
design.design.__init__(self, name) super().__init__(name)
debug.info(2, "Create sense_amp") debug.info(2, "Create sense_amp")
self.width = sense_amp.width self.width = sense_amp.width

View File

@ -22,7 +22,7 @@ class sense_amp_array(design.design):
def __init__(self, name, word_size, words_per_row, num_spare_cols=None, column_offset=0): def __init__(self, name, word_size, words_per_row, num_spare_cols=None, column_offset=0):
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("word_size {0}".format(word_size)) self.add_comment("word_size {0}".format(word_size))
self.add_comment("words_per_row: {0}".format(words_per_row)) self.add_comment("words_per_row: {0}".format(words_per_row))

View File

@ -21,7 +21,7 @@ class single_level_column_mux_array(design.design):
""" """
def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0): def __init__(self, name, columns, word_size, bitcell_bl="bl", bitcell_br="br", column_offset=0):
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br)) self.add_comment("cols: {0} word_size: {1} bl: {2} br: {3}".format(columns, word_size, bitcell_bl, bitcell_br))

View File

@ -19,7 +19,7 @@ class tri_gate_array(design.design):
def __init__(self, columns, word_size, name): def __init__(self, columns, word_size, name):
"""Intial function of tri gate array """ """Intial function of tri gate array """
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.columns = columns self.columns = columns

View File

@ -0,0 +1,140 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import design
from tech import drc, layer
from vector import vector
from sram_factory import factory
from globals import OPTS
class wordline_buffer_array(design.design):
"""
Creates a Wordline Buffer/Inverter array
"""
def __init__(self, name, rows, cols):
design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.rows = rows
self.cols = cols
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_modules()
self.add_pins()
self.create_drivers()
def create_layout(self):
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
self.place_drivers()
self.route_layout()
self.route_vdd_gnd()
self.offset_all_coordinates()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
# inputs to wordline_driver.
for i in range(self.rows):
self.add_pin("in_{0}".format(i), "INPUT")
# Outputs from wordline_driver.
for i in range(self.rows):
self.add_pin("out_{0}".format(i), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
b = factory.create(module_type="bitcell")
self.wl_driver = factory.create(module_type="inv_dec",
size=self.cols,
height=b.height)
self.add_mod(self.wl_driver)
def route_vdd_gnd(self):
"""
Add a pin for each row of vdd/gnd which
are must-connects next level up.
"""
if OPTS.tech_name == "sky130":
for name in ["vdd", "gnd"]:
supply_pins = self.wld_inst[0].get_pins(name)
for pin in supply_pins:
self.add_layout_pin_segment_center(text=name,
layer=pin.layer,
start=pin.bc(),
end=vector(pin.cx(), self.height))
else:
# Find the x offsets for where the vias/pins should be placed
xoffset_list = [self.wld_inst[0].rx()]
for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the and's too
(gate_offset, y_dir) = self.get_gate_offset(0,
self.wl_driver.height,
num)
# Route both supplies
for name in ["vdd", "gnd"]:
supply_pin = self.wld_inst[num].get_pin(name)
# Add pins in two locations
for xoffset in xoffset_list:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_power_pin(name, pin_pos)
def create_drivers(self):
self.wld_inst = []
for row in range(self.rows):
self.wld_inst.append(self.add_inst(name="wld{0}".format(row),
mod=self.wl_driver))
self.connect_inst(["in_{0}".format(row),
"out_{0}".format(row),
"vdd", "gnd"])
def place_drivers(self):
for row in range(self.rows):
if (row % 2):
y_offset = self.wl_driver.height * (row + 1)
inst_mirror = "MX"
else:
y_offset = self.wl_driver.height * row
inst_mirror = "R0"
offset = [0, y_offset]
self.wld_inst[row].place(offset=offset,
mirror=inst_mirror)
self.width = self.wl_driver.width
self.height = self.wl_driver.height * self.rows
def route_layout(self):
""" Route all of the signals """
for row in range(self.rows):
inst = self.wld_inst[row]
self.copy_layout_pin(inst, "A", "in_{0}".format(row))
# output each WL on the right
wl_offset = inst.get_pin("Z").rc()
self.add_layout_pin_segment_center(text="out_{0}".format(row),
layer=self.route_layer,
start=wl_offset,
end=wl_offset - vector(self.m1_width, 0))

View File

@ -12,6 +12,7 @@ from vector import vector
from sram_factory import factory from sram_factory import factory
from globals import OPTS from globals import OPTS
class wordline_driver_array(design.design): class wordline_driver_array(design.design):
""" """
Creates a Wordline Driver Creates a Wordline Driver
@ -19,7 +20,7 @@ class wordline_driver_array(design.design):
""" """
def __init__(self, name, rows, cols): def __init__(self, name, rows, cols):
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols))

View File

@ -21,7 +21,7 @@ class write_driver_array(design.design):
def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None, column_offset=0): def __init__(self, name, columns, word_size, num_spare_cols=None, write_size=None, column_offset=0):
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns)) self.add_comment("columns: {0}".format(columns))
self.add_comment("word_size {0}".format(word_size)) self.add_comment("word_size {0}".format(word_size))

View File

@ -19,7 +19,7 @@ class write_mask_and_array(design.design):
""" """
def __init__(self, name, columns, word_size, write_size, column_offset=0): def __init__(self, name, columns, word_size, write_size, column_offset=0):
design.design.__init__(self, name) super().__init__(name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns)) self.add_comment("columns: {0}".format(columns))
self.add_comment("word_size {0}".format(word_size)) self.add_comment("word_size {0}".format(word_size))

View File

@ -22,7 +22,7 @@ class pand2(pgate.pgate):
self.vertical = vertical self.vertical = vertical
self.size = size self.size = size
pgate.pgate.__init__(self, name, height, add_wells) super().__init__(name, height, add_wells)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()

View File

@ -23,7 +23,7 @@ class pand3(pgate.pgate):
self.size = size self.size = size
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) super().__init__(name, height, add_wells)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()

View File

@ -25,7 +25,7 @@ class pbuf(pgate.pgate):
self.height = height self.height = height
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) super().__init__(name, height)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()

125
compiler/pgates/pbuf_dec.py Normal file
View File

@ -0,0 +1,125 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from vector import vector
import pgate
from sram_factory import factory
class pbuf_dec(pgate.pgate):
"""
This is a simple buffer used for driving wordlines.
"""
def __init__(self, name, size=4, height=None):
debug.info(1, "creating {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size))
self.stage_effort = 4
self.size = size
self.height = height
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_layout(self):
self.width = self.inv1.width + self.inv2.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("Z", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1, int(self.size / self.stage_effort))
self.inv1 = factory.create(module_type="pinv_dec",
size=input_size,
height=self.height)
self.add_mod(self.inv1)
self.inv2 = factory.create(module_type="pinv_dec",
size=self.size,
height=self.height)
self.add_mod(self.inv2)
def create_insts(self):
self.inv1_inst = self.add_inst(name="buf_inv1",
mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add INV1 to the right
self.inv1_inst.place(vector(0, 0))
# Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
def add_wires(self):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_loc = vector(a2_pin.cx(), z1_pin.cy())
self.add_path(self.route_layer,
[z1_pin.rc(), mid_loc, a2_pin.lc()],
width=a2_pin.width())
def route_supply_rails(self):
""" Add vdd/gnd rails to the top, (middle), and bottom. """
self.copy_layout_pin(self.inv1_inst, "vdd")
self.copy_layout_pin(self.inv1_inst, "gnd")
self.copy_layout_pin(self.inv2_inst, "vdd")
self.copy_layout_pin(self.inv2_inst, "gnd")
def add_layout_pins(self):
z_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=z_pin.layer,
offset=z_pin.center(),
width=z_pin.width(),
height=z_pin.height())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer=a_pin.layer,
offset=a_pin.center(),
width=a_pin.width(),
height=a_pin.height())
def get_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the A -> Z path"""
stage_effort_list = []
stage1_cout = self.inv2.get_cin()
stage1 = self.inv1.get_stage_effort(stage1_cout, inp_is_rise)
stage_effort_list.append(stage1)
last_stage_is_rise = stage1.is_rise
stage2 = self.inv2.get_stage_effort(external_cout, last_stage_is_rise)
stage_effort_list.append(stage2)
return stage_effort_list
def get_cin(self):
"""Returns the relative capacitance of the input"""
input_cin = self.inv1.get_cin()
return input_cin

View File

@ -35,7 +35,7 @@ class pdriver(pgate.pgate):
debug.error("Cannot specify both size_list and inverting.", -1) debug.error("Cannot specify both size_list and inverting.", -1)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) super().__init__(name, height, add_wells)
def compute_sizes(self): def compute_sizes(self):
# size_list specified # size_list specified

View File

@ -26,7 +26,7 @@ class pgate(design.design):
def __init__(self, name, height=None, add_wells=True): def __init__(self, name, height=None, add_wells=True):
""" Creates a generic cell """ """ Creates a generic cell """
design.design.__init__(self, name) super().__init__(name)
if height: if height:
self.height = height self.height = height

View File

@ -44,7 +44,7 @@ class pinv(pgate.pgate):
self.pmos_size = beta * size self.pmos_size = beta * size
self.beta = beta self.beta = beta
pgate.pgate.__init__(self, name, height, add_wells) super().__init__(name, height, add_wells)
def create_netlist(self): def create_netlist(self):
""" Calls all functions related to the generation of the netlist """ """ Calls all functions related to the generation of the netlist """

View File

@ -38,7 +38,7 @@ class pinv_dec(pinv.pinv):
else: else:
self.supply_layer = "m2" self.supply_layer = "m2"
pinv.pinv.__init__(self, name, size, beta, self.cell_height, add_wells) super().__init__(name, size, beta, self.cell_height, add_wells)
def determine_tx_mults(self): def determine_tx_mults(self):
""" """

View File

@ -32,7 +32,7 @@ class pinvbuf(pgate.pgate):
self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1) self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name) super().__init__(name)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()

View File

@ -43,7 +43,7 @@ class pnand2(pgate.pgate):
self.pmos_width = self.nearest_bin("pmos", self.pmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) super().__init__(name, height, add_wells)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()

View File

@ -46,7 +46,7 @@ class pnand3(pgate.pgate):
self.pmos_width = self.nearest_bin("pmos", self.pmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) super().__init__(name, height, add_wells)
def add_pins(self): def add_pins(self):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """

View File

@ -42,7 +42,7 @@ class pnor2(pgate.pgate):
self.pmos_width = self.nearest_bin("pmos", self.pmos_width) self.pmos_width = self.nearest_bin("pmos", self.pmos_width)
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height, add_wells) super().__init__(name, height, add_wells)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()

View File

@ -23,7 +23,7 @@ class precharge(design.design):
def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"): def __init__(self, name, size=1, bitcell_bl="bl", bitcell_br="br"):
debug.info(2, "creating precharge cell {0}".format(name)) debug.info(2, "creating precharge cell {0}".format(name))
design.design.__init__(self, name) super().__init__(name)
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
self.beta = parameter["beta"] self.beta = parameter["beta"]

View File

@ -38,7 +38,7 @@ class ptristate_inv(pgate.pgate):
self.pmos_width = self.pmos_size * drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) super().__init__(name, height)
def create_netlist(self): def create_netlist(self):
""" Calls all functions related to the generation of the netlist """ """ Calls all functions related to the generation of the netlist """

View File

@ -75,7 +75,7 @@ class ptx(design.design):
# replace periods with underscore for newer spice compatibility # replace periods with underscore for newer spice compatibility
name = name.replace('.', '_') name = name.replace('.', '_')
debug.info(3, "creating ptx {0}".format(name)) debug.info(3, "creating ptx {0}".format(name))
design.design.__init__(self, name) super().__init__(name)
self.tx_type = tx_type self.tx_type = tx_type
self.mults = mults self.mults = mults
@ -129,7 +129,7 @@ class ptx(design.design):
# be decided in the layout later. # be decided in the layout later.
area_sd = 2.5 * self.poly_width * self.tx_width area_sd = 2.5 * self.poly_width * self.tx_width
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": if OPTS.tech_name == "sky130":
# sky130 simulation cannot use the mult parameter in simulation # sky130 simulation cannot use the mult parameter in simulation
(self.tx_width, self.mults) = pgate.best_bin(self.tx_type, self.tx_width) (self.tx_width, self.mults) = pgate.best_bin(self.tx_type, self.tx_width)
main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
@ -148,20 +148,19 @@ class ptx(design.design):
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd) area_sd)
self.spice_device = main_str + area_str self.spice_device = main_str + area_str
self.spice.append("\n* ptx " + self.spice_device) self.spice.append("\n* spice ptx " + self.spice_device)
if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre": if OPTS.tech_name == "sky130" and OPTS.lvs_exe and OPTS.lvs_exe[0] == "calibre":
# sky130 requires mult parameter too # sky130 requires mult parameter too
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type], self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} mult={1}".format(spice[self.tx_type],
self.mults, self.mults,
self.tx_width, self.tx_width,
drc("minwidth_poly")) drc("minwidth_poly"))
else: else:
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults, self.mults,
self.tx_width, self.tx_width,
drc("minwidth_poly")) drc("minwidth_poly"))
def setup_layout_constants(self): def setup_layout_constants(self):
""" """
Pre-compute some handy layout parameters. Pre-compute some handy layout parameters.

View File

@ -22,7 +22,7 @@ class pwrite_driver(design.design):
def __init__(self, name, size=0): def __init__(self, name, size=0):
debug.error("pwrite_driver not implemented yet.", -1) debug.error("pwrite_driver not implemented yet.", -1)
debug.info(1, "creating pwrite_driver {}".format(name)) debug.info(1, "creating pwrite_driver {}".format(name))
design.design.__init__(self, name) super().__init__(name)
self.size = size self.size = size
self.beta = parameter["beta"] self.beta = parameter["beta"]
self.pmos_width = self.beta*self.size*parameter["min_tx_size"] self.pmos_width = self.beta*self.size*parameter["min_tx_size"]

View File

@ -30,7 +30,7 @@ class single_level_column_mux(pgate.pgate):
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
pgate.pgate.__init__(self, name) super().__init__(name)
def get_bl_names(self): def get_bl_names(self):
return "bl" return "bl"

View File

@ -21,7 +21,7 @@ class wordline_driver(design.design):
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
debug.info(1, "Creating wordline_driver {}".format(name)) debug.info(1, "Creating wordline_driver {}".format(name))
self.add_comment("size: {}".format(size)) self.add_comment("size: {}".format(size))
design.design.__init__(self, name) super().__init__(name)
if height is None: if height is None:
b = factory.create(module_type="bitcell") b = factory.create(module_type="bitcell")

View File

@ -90,9 +90,9 @@ class sram():
# Save the LVS file # Save the LVS file
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
spname = OPTS.output_path + self.s.name + ".lvs" lvsname = OPTS.output_path + self.s.name + ".lvs.sp"
debug.print_raw("LVS: Writing to {0}".format(spname)) debug.print_raw("LVS: Writing to {0}".format(lvsname))
self.lvs_write(spname) self.lvs_write(lvsname)
print_time("LVS writing", datetime.datetime.now(), start_time) print_time("LVS writing", datetime.datetime.now(), start_time)
# Save the extracted spice file # Save the extracted spice file
@ -100,8 +100,10 @@ class sram():
import verify import verify
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
# Output the extracted design if requested # Output the extracted design if requested
sp_file = OPTS.output_path + "temp_pex.sp" pexname = OPTS.output_path + self.s.name + ".pex.sp"
verify.run_pex(self.s.name, gdsname, spname, output=sp_file) spname = OPTS.output_path + self.s.name + ".sp"
verify.run_pex(self.s.name, gdsname, spname, output=pexname)
sp_file = pexname
print_time("Extraction", datetime.datetime.now(), start_time) print_time("Extraction", datetime.datetime.now(), start_time)
else: else:
# Use generated spice file for characterization # Use generated spice file for characterization

View File

@ -160,7 +160,10 @@ class sram_1bank(sram_base):
port = 0 port = 0
# Add the col address flops below the bank to the right of the control logic # Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].rx() + self.dff.width x_offset = self.control_logic_insts[port].rx() + self.dff.width
y_offset = - self.data_bus_size[port] - self.dff.height # Place it a data bus below the x-axis, but at least as low as the control logic to not block
# the control logic signals
y_offset = min(-self.data_bus_size[port] - self.dff.height,
self.control_logic_insts[port].by())
if self.col_addr_dff: if self.col_addr_dff:
self.col_addr_pos[port] = vector(x_offset, self.col_addr_pos[port] = vector(x_offset,
y_offset) y_offset)
@ -201,7 +204,10 @@ class sram_1bank(sram_base):
# Add the col address flops below the bank to the right of the control logic # Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width
y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height # Place it a data bus below the x-axis, but at least as high as the control logic to not block
# the control logic signals
y_offset = max(self.bank.height + self.data_bus_size[port] + self.dff.height,
self.control_logic_insts[port].uy() - self.dff.height)
if self.col_addr_dff: if self.col_addr_dff:
self.col_addr_pos[port] = vector(x_offset, self.col_addr_pos[port] = vector(x_offset,
y_offset) y_offset)

View File

@ -414,6 +414,7 @@ class sram_base(design, verilog, lef):
temp.append("dout{0}[{1}]".format(port, bit)) temp.append("dout{0}[{1}]".format(port, bit))
for port in self.all_ports: for port in self.all_ports:
temp.append("rbl_bl{0}".format(port)) temp.append("rbl_bl{0}".format(port))
temp.append("rbl_br{0}".format(port))
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
temp.append("bank_din{0}[{1}]".format(port, bit)) temp.append("bank_din{0}[{1}]".format(port, bit))

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class pbuf_dec_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1
OPTS.num_w_ports = 0
globals.setup_bitcell()
debug.info(2, "Checking 8x size decoder buffer")
a = factory.create(module_type="pbuf_dec", size=8)
self.local_check(a)
globals.end_openram()
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -21,7 +21,7 @@ class pbuf_test(openram_test):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME")) config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file) globals.init_openram(config_file)
debug.info(2, "Testing inverter/buffer 4x 8x") debug.info(2, "Testing buffer 8x")
a = factory.create(module_type="pbuf", size=8) a = factory.create(module_type="pbuf", size=8)
self.local_check(a) self.local_check(a)

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from sram_factory import factory
import debug
#@unittest.skip("SKIPPING 05_local_bitcell_array_test")
class local_bitcell_array_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
debug.info(2, "Testing 4x4 local bitcell array for 6t_cell without replica")
a = factory.create(module_type="local_bitcell_array", cols=4, rows=4, ports=[0], add_replica=False)
self.local_check(a)
debug.info(2, "Testing 4x4 local bitcell array for 6t_cell with replica column")
a = factory.create(module_type="local_bitcell_array", cols=4, left_rbl=1, rows=4, ports=[0])
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,37 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class wordline_buffer_array_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check wordline driver for single port
debug.info(2, "Checking driver")
tx = factory.create(module_type="wordline_buffer_array", rows=8, cols=32)
self.local_check(tx)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -24,14 +24,35 @@ class replica_bitcell_array_1rw_1r_test(openram_test):
OPTS.num_w_ports = 0 OPTS.num_w_ports = 0
globals.setup_bitcell() globals.setup_bitcell()
debug.info(2, "Testing 4x4 array for cell_1rw_1r") debug.info(2, "Testing 4x4 non-replica array for cell_1rw_1r")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=1, right_rbl=1, bitcell_ports=[0, 1]) a = factory.create(module_type="replica_bitcell_array",
cols=4,
rows=4,
left_rbl=1,
right_rbl=1,
bitcell_ports=[0, 1],
add_replica=False)
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 array for cell_1rw_1r")
a = factory.create(module_type="replica_bitcell_array",
cols=4,
rows=4,
left_rbl=1,
right_rbl=1,
bitcell_ports=[0, 1])
self.local_check(a)
# Sky 130 has restrictions on the symmetries # Sky 130 has restrictions on the symmetries
if OPTS.tech_name != "sky130": if OPTS.tech_name != "sky130":
debug.info(2, "Testing 4x4 array for cell_1rw_1r") debug.info(2, "Testing 4x4 array for cell_1rw_1r")
a = factory.create(module_type="replica_bitcell_array", cols=4, rows=4, left_rbl=2, right_rbl=0, bitcell_ports=[0, 1]) a = factory.create(module_type="replica_bitcell_array",
cols=4,
rows=4,
left_rbl=2,
right_rbl=0,
bitcell_ports=[0, 1])
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -30,7 +30,7 @@ num_lvs_runs = 0
num_pex_runs = 0 num_pex_runs = 0
def write_calibre_drc_script(cell_name, extract, final_verification): def write_calibre_drc_script(cell_name, extract, final_verification, gds_name):
""" Write a Calibre runset file and script to run DRC """ """ Write a Calibre runset file and script to run DRC """
# the runset file contains all the options to run calibre # the runset file contains all the options to run calibre
from tech import drc from tech import drc
@ -39,7 +39,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification):
drc_runset = { drc_runset = {
'drcRulesFile': drc_rules, 'drcRulesFile': drc_rules,
'drcRunDir': OPTS.openram_temp, 'drcRunDir': OPTS.openram_temp,
'drcLayoutPaths': cell_name + ".gds", 'drcLayoutPaths': gds_name,
'drcLayoutPrimary': cell_name, 'drcLayoutPrimary': cell_name,
'drcLayoutSystem': 'GDSII', 'drcLayoutSystem': 'GDSII',
'drcResultsformat': 'ASCII', 'drcResultsformat': 'ASCII',
@ -68,7 +68,7 @@ def write_calibre_drc_script(cell_name, extract, final_verification):
return drc_runset return drc_runset
def write_calibre_lvs_script(cell_name, final_verification): def write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name):
""" Write a Calibre runset file and script to run LVS """ """ Write a Calibre runset file and script to run LVS """
from tech import drc from tech import drc
@ -76,9 +76,9 @@ def write_calibre_lvs_script(cell_name, final_verification):
lvs_runset = { lvs_runset = {
'lvsRulesFile': lvs_rules, 'lvsRulesFile': lvs_rules,
'lvsRunDir': OPTS.openram_temp, 'lvsRunDir': OPTS.openram_temp,
'lvsLayoutPaths': cell_name + ".gds", 'lvsLayoutPaths': gds_name,
'lvsLayoutPrimary': cell_name, 'lvsLayoutPrimary': cell_name,
'lvsSourcePath': cell_name + ".sp", 'lvsSourcePath': sp_name,
'lvsSourcePrimary': cell_name, 'lvsSourcePrimary': cell_name,
'lvsSourceSystem': 'SPICE', 'lvsSourceSystem': 'SPICE',
'lvsSpiceFile': "{}.spice".format(cell_name), 'lvsSpiceFile': "{}.spice".format(cell_name),
@ -135,11 +135,11 @@ def write_calibre_lvs_script(cell_name, final_verification):
def write_calibre_pex_script(cell_name, extract, output, final_verification): def write_calibre_pex_script(cell_name, extract, output, final_verification):
""" Write a pex script that can either just extract the netlist or the netlist+parasitics """ """ Write a pex script that can either just extract the netlist or the netlist+parasitics """
if output == None: if output == None:
output = name + ".pex.netlist" output = cell_name + ".pex.sp"
# check if lvs report has been done # check if lvs report has been done
# if not run drc and lvs # if not run drc and lvs
if not os.path.isfile(cell_name + ".lvs.report"): if not os.path.isfile(OPTS.openram_temp + cell_name + ".lvs.report"):
gds_name = OPTS.openram_temp +"/"+ cell_name + ".gds" gds_name = OPTS.openram_temp +"/"+ cell_name + ".gds"
sp_name = OPTS.openram_temp +"/"+ cell_name + ".sp" sp_name = OPTS.openram_temp +"/"+ cell_name + ".sp"
run_drc(cell_name, gds_name) run_drc(cell_name, gds_name)
@ -155,7 +155,7 @@ def write_calibre_pex_script(cell_name, extract, output, final_verification):
'pexSourcePath': cell_name + ".sp", 'pexSourcePath': cell_name + ".sp",
'pexSourcePrimary': cell_name, 'pexSourcePrimary': cell_name,
'pexReportFile': cell_name + ".pex.report", 'pexReportFile': cell_name + ".pex.report",
'pexPexNetlistFile': cell_name + ".pex.netlist", 'pexPexNetlistFile': output,
'pexPexReportFile': cell_name + ".pex.report", 'pexPexReportFile': cell_name + ".pex.report",
'pexMaskDBFile': cell_name + ".maskdb", 'pexMaskDBFile': cell_name + ".maskdb",
'cmnFDIDEFLayoutPath': cell_name + ".def", 'cmnFDIDEFLayoutPath': cell_name + ".def",
@ -195,11 +195,14 @@ def run_drc(cell_name, gds_name, extract=False, final_verification=False):
filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds") filter_gds(cell_name, OPTS.openram_temp + "temp.gds", OPTS.openram_temp + cell_name + ".gds")
else: else:
# Copy file to local dir if it isn't already # Copy file to local dir if it isn't already
if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp) shutil.copy(gds_name, OPTS.openram_temp)
drc_runset = write_calibre_drc_script(cell_name, extract, final_verification) drc_runset = write_calibre_drc_script(cell_name, extract, final_verification, gds_name)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp + os.path.basename(gds_name))
(outfile, errfile, resultsfile) = run_script(cell_name, "drc") (outfile, errfile, resultsfile) = run_script(cell_name, "drc")
# check the result for these lines in the summary: # check the result for these lines in the summary:
@ -238,12 +241,12 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
global num_lvs_runs global num_lvs_runs
num_lvs_runs += 1 num_lvs_runs += 1
lvs_runset = write_calibre_lvs_script(cell_name, final_verification) lvs_runset = write_calibre_lvs_script(cell_name, final_verification, gds_name, sp_name)
# Copy file to local dir if it isn't already # Copy file to local dir if it isn't already
if os.path.dirname(gds_name)!=OPTS.openram_temp.rstrip('/'): if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp) shutil.copy(gds_name, OPTS.openram_temp)
if os.path.dirname(sp_name)!=OPTS.openram_temp.rstrip('/'): if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
shutil.copy(sp_name, OPTS.openram_temp) shutil.copy(sp_name, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(cell_name, "lvs") (outfile, errfile, resultsfile) = run_script(cell_name, "lvs")
@ -325,8 +328,14 @@ def run_pex(cell_name, gds_name, sp_name, output=None, final_verification=False)
global num_pex_runs global num_pex_runs
num_pex_runs += 1 num_pex_runs += 1
write_calibre_pex_script(cell_name,True,output,final_verification) write_calibre_pex_script(cell_name, True, output, final_verification)
# Copy file to local dir if it isn't already
if not os.path.isfile(OPTS.openram_temp + os.path.basename(gds_name)):
shutil.copy(gds_name, OPTS.openram_temp)
if not os.path.isfile(OPTS.openram_temp + os.path.basename(sp_name)):
shutil.copy(sp_name, OPTS.openram_temp)
(outfile, errfile, resultsfile) = run_script(cell_name, "pex") (outfile, errfile, resultsfile) = run_script(cell_name, "pex")

View File

@ -77,6 +77,8 @@ def write_magic_script(cell_name, extract=False, final_verification=False):
f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1])) f.write("{} -dnull -noconsole << EOF\n".format(OPTS.drc_exe[1]))
f.write("gds polygon subcell true\n") f.write("gds polygon subcell true\n")
f.write("gds warning default\n") f.write("gds warning default\n")
# This causes substrate contacts to not be extracted
f.write("# gds readonly true\n")
f.write("gds read {}.gds\n".format(cell_name)) f.write("gds read {}.gds\n".format(cell_name))
f.write("load {}\n".format(cell_name)) f.write("load {}\n".format(cell_name))
# Flatten the cell to get rid of DRCs spanning multiple layers # Flatten the cell to get rid of DRCs spanning multiple layers

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<technology>
<name>SCMOS</name>
<description/>
<dbu>0.001</dbu>
<base-path/>
<layer-properties_file>mosis.lyp</layer-properties_file>
<add-other-layers>true</add-other-layers>
<connectivity>
<connection>Active,ActX,Metal1</connection>
<connection>Poly1,P1Con,Metal1</connection>
<connection>Metal1,Via,Metal2</connection>
<connection>Metal2,Via2,Metal3</connection>
<connection>Metal3,Via3,Metal4</connection>
<symbols>Active='43/0'</symbols>
<symbols>Poly1='46/0'</symbols>
<symbols>P1Con='47/0'</symbols>
<symbols>ActX='48/0'</symbols>
<symbols>Metal1='49/0'</symbols>
<symbols>Via='50/0'</symbols>
<symbols>Metal2='51/0'</symbols>
<symbols>Via2='61/0'</symbols>
<symbols>Metal3.='62/0'</symbols>
<symbols>Via3='30/0'</symbols>
<symbols>Metal4='31/0'</symbols>
</connectivity>
</technology>