mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into pex
This commit is contained in:
commit
9cecf367ee
|
|
@ -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).
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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]
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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)))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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}"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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."""
|
||||||
|
|
|
||||||
|
|
@ -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 """
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
@ -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))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 """
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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 """
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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 """
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
@ -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())
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
Loading…
Reference in New Issue