merge local with dev

This commit is contained in:
jcirimel 2020-04-16 02:16:56 -07:00
commit ebb1a7bedb
13 changed files with 441 additions and 281 deletions

15
compiler/base/errors.py Normal file
View File

@ -0,0 +1,15 @@
class drc_error(Exception):
"""Exception raised for DRC errors.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
# def __init__(self, expression, message):
# self.expression = expression
# self.message = message
def __init__(self, message):
self.message = message

View File

@ -589,4 +589,4 @@ def report_status():
if OPTS.trim_netlist: if OPTS.trim_netlist:
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).") debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")
if OPTS.nominal_corner_only: if OPTS.nominal_corner_only:
debug.print_raw("Only characterizing nominal corner.") debug.print_raw("Only characterizing nominal corner.")

View File

@ -45,11 +45,9 @@ class bank_select(design.design):
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
self.width = max([x.rx() for x in self.inv_inst]) self.width = max([x.rx() for x in self.inv_inst])
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
# Number of control lines in the bus # Number of control lines in the bus
@ -65,19 +63,18 @@ class bank_select(design.design):
if (self.port == "rw") or (self.port == "r"): if (self.port == "rw") or (self.port == "r"):
self.input_control_signals.append("s_en") self.input_control_signals.append("s_en")
# These will be outputs of the gaters if this is multibank # These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_"+str for str in self.input_control_signals] self.control_signals = ["gated_" + str for str in self.input_control_signals]
self.add_pin_list(self.input_control_signals, "INPUT") self.add_pin_list(self.input_control_signals, "INPUT")
self.add_pin("bank_sel") self.add_pin("bank_sel")
self.add_pin_list(self.control_signals, "OUTPUT") self.add_pin_list(self.control_signals, "OUTPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
""" Create modules for later instantiation """ """ Create modules for later instantiation """
self.bitcell = factory.create(module_type="bitcell") self.dff = factory.create(module_type="dff")
height = self.dff.height + drc("poly_to_active")
height = self.bitcell.height + drc("poly_to_active")
# 1x Inverter # 1x Inverter
self.inv_sel = factory.create(module_type="pinv", height=height) self.inv_sel = factory.create(module_type="pinv", height=height)
@ -98,17 +95,15 @@ class bank_select(design.design):
def calculate_module_offsets(self): def calculate_module_offsets(self):
self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_bank_sel_inv = 0 self.xoffset_bank_sel_inv = 0
self.xoffset_inputs = 0 self.xoffset_inputs = 0
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
def create_instances(self): def create_instances(self):
self.bank_sel_inv=self.add_inst(name="bank_sel_inv", self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
mod=self.inv_sel) mod=self.inv_sel)
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
@ -125,36 +120,36 @@ class bank_select(design.design):
# (writes occur on clk low) # (writes occur on clk low)
if input_name in ("clk_buf"): if input_name in ("clk_buf"):
self.logic_inst.append(self.add_inst(name=name_nor, self.logic_inst.append(self.add_inst(name=name_nor,
mod=self.nor2)) mod=self.nor2))
self.connect_inst([input_name, self.connect_inst([input_name,
"bank_sel_bar", "bank_sel_bar",
gated_name+"_temp_bar", gated_name + "_temp_bar",
"vdd", "vdd",
"gnd"]) "gnd"])
# They all get inverters on the output # They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv, self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x_nor)) mod=self.inv4x_nor))
self.connect_inst([gated_name+"_temp_bar", self.connect_inst([gated_name + "_temp_bar",
gated_name, gated_name,
"vdd", "vdd",
"gnd"]) "gnd"])
# the rest are AND (nand2+inv) gates # the rest are AND (nand2+inv) gates
else: else:
self.logic_inst.append(self.add_inst(name=name_nand, self.logic_inst.append(self.add_inst(name=name_nand,
mod=self.nand2)) mod=self.nand2))
self.connect_inst([input_name, self.connect_inst([input_name,
"bank_sel", "bank_sel",
gated_name+"_temp_bar", gated_name + "_temp_bar",
"vdd", "vdd",
"gnd"]) "gnd"])
# They all get inverters on the output # They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv, self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x)) mod=self.inv4x))
self.connect_inst([gated_name+"_temp_bar", self.connect_inst([gated_name + "_temp_bar",
gated_name, gated_name,
"vdd", "vdd",
"gnd"]) "gnd"])
@ -177,9 +172,9 @@ class bank_select(design.design):
if i == 0: if i == 0:
y_offset = 0 y_offset = 0
else: else:
y_offset = self.inv4x_nor.height + self.inv4x.height * (i-1) y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
if i%2: if i % 2:
y_offset += self.inv4x.height y_offset += self.inv4x.height
mirror = "MX" mirror = "MX"
else: else:
@ -200,7 +195,6 @@ class bank_select(design.design):
# They all get inverters on the output # They all get inverters on the output
inv_inst.place(offset=[logic_inst.rx(), y_offset], inv_inst.place(offset=[logic_inst.rx(), y_offset],
mirror=mirror) mirror=mirror)
def route_instances(self): def route_instances(self):
@ -222,57 +216,56 @@ class bank_select(design.design):
end=bank_sel_pin_end) end=bank_sel_pin_end)
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=bank_sel_pin_end, offset=bank_sel_pin_end,
directions=("H","H")) directions=("H", "H"))
# bank_sel_bar is vertical wire # bank_sel_bar is vertical wire
bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z")
xoffset_bank_sel_bar = bank_sel_bar_pin.rx() xoffset_bank_sel_bar = bank_sel_bar_pin.rx()
self.add_label_pin(text="bank_sel_bar", self.add_label_pin(text="bank_sel_bar",
layer="m2", layer="m2",
offset=vector(xoffset_bank_sel_bar, 0), offset=vector(xoffset_bank_sel_bar, 0),
height=self.inv4x.height) height=self.inv4x.height)
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=bank_sel_bar_pin.rc()) offset=bank_sel_bar_pin.rc())
for i in range(self.num_control_lines): for i in range(self.num_control_lines):
logic_inst = self.logic_inst[i] logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i] inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i] input_name = self.input_control_signals[i]
gated_name = self.control_signals[i] gated_name = self.control_signals[i]
if input_name in ("clk_buf"): if input_name in ("clk_buf"):
xoffset_bank_signal = xoffset_bank_sel_bar xoffset_bank_signal = xoffset_bank_sel_bar
else: else:
xoffset_bank_signal = xoffset_bank_sel xoffset_bank_signal = xoffset_bank_sel
# Connect the logic output to inverter input # Connect the logic output to inverter input
pre = logic_inst.get_pin("Z").lc() out_pin = logic_inst.get_pin("Z")
out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0) out_pos = out_pin.rc()
in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0) in_pin = inv_inst.get_pin("A")
post = inv_inst.get_pin("A").rc() in_pos = in_pin.lc()
self.add_path("m1", [pre, out_position, in_position, post]) mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
# Connect the logic B input to bank_sel / bank_sel_bar
# Connect the logic B input to bank_sel/bank_sel_bar logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0)
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1_via.height,0)
input_pos = vector(xoffset_bank_signal, logic_pos.y) input_pos = vector(xoffset_bank_signal, logic_pos.y)
self.add_path("m2",[logic_pos, input_pos]) self.add_path("m2", [logic_pos, input_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=logic_pos, offset=logic_pos,
directions=("H","H")) directions=("H", "H"))
# Connect the logic A input to the input pin # Connect the logic A input to the input pin
logic_pos = logic_inst.get_pin("A").lc() logic_pos = logic_inst.get_pin("A").lc()
input_pos = vector(0,logic_pos.y) input_pos = vector(0, logic_pos.y)
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=logic_pos, offset=logic_pos,
directions=("H","H")) directions=("H", "H"))
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=logic_pos, offset=logic_pos,
directions=("H","H")) directions=("H", "H"))
self.add_layout_pin_segment_center(text=input_name, self.add_layout_pin_segment_center(text=input_name,
layer="m3", layer="m3",
start=input_pos, start=input_pos,
@ -286,7 +279,6 @@ class bank_select(design.design):
width=inv_inst.rx() - out_pin.lx(), width=inv_inst.rx() - out_pin.lx(),
height=out_pin.height()) height=out_pin.height())
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
a_xoffset = self.logic_inst[0].lx() a_xoffset = self.logic_inst[0].lx()
b_xoffset = self.inv_inst[0].lx() b_xoffset = self.inv_inst[0].lx()
@ -294,7 +286,7 @@ class bank_select(design.design):
# Route both supplies # Route both supplies
for n in ["vdd", "gnd"]: for n in ["vdd", "gnd"]:
supply_pin = self.inv_inst[num].get_pin(n) supply_pin = self.inv_inst[num].get_pin(n)
supply_offset = supply_pin.ll().scale(0,1) supply_offset = supply_pin.ll().scale(0, 1)
self.add_rect(layer="m1", self.add_rect(layer="m1",
offset=supply_offset, offset=supply_offset,
width=self.width) width=self.width)
@ -304,10 +296,10 @@ class bank_select(design.design):
pin_pos = vector(xoffset, supply_pin.cy()) pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pin_pos, offset=pin_pos,
directions=("H","H")) directions=("H", "H"))
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=pin_pos, offset=pin_pos,
directions=("H","H")) directions=("H", "H"))
self.add_layout_pin_rect_center(text=n, self.add_layout_pin_rect_center(text=n,
layer="m3", layer="m3",
offset=pin_pos) offset=pin_pos)

View File

@ -11,13 +11,15 @@ import math
from sram_factory import factory from sram_factory import factory
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from errors import drc_error
from tech import cell_properties
class hierarchical_decoder(design.design): class hierarchical_decoder(design.design):
""" """
Dynamically generated hierarchical decoder. Dynamically generated hierarchical decoder.
""" """
def __init__(self, name, rows): def __init__(self, name, num_outputs):
design.design.__init__(self, name) design.design.__init__(self, name)
self.AND_FORMAT = "DEC_AND_{0}" self.AND_FORMAT = "DEC_AND_{0}"
@ -25,9 +27,17 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
(self.cell_height, self.cell_multiple) = self.find_decoder_height() b = factory.create(module_type="bitcell")
self.rows = rows try:
self.num_inputs = math.ceil(math.log(self.rows, 2)) self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
except AttributeError:
self.cell_multiple = 1
# For debugging
# self.cell_multiple = 2
self.cell_height = self.cell_multiple * b.height
self.num_outputs = num_outputs
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
self.create_netlist() self.create_netlist()
@ -35,20 +45,37 @@ class hierarchical_decoder(design.design):
self.create_layout() self.create_layout()
def find_decoder_height(self): def find_decoder_height(self):
"""
Dead code. This would dynamically determine the bitcell multiple,
but I just decided to hard code it in the tech file if it is not 1
because a DRC tool would be required even to run in front-end mode.
"""
b = factory.create(module_type="bitcell") b = factory.create(module_type="bitcell")
# Old behavior # Old behavior
return (b.height, 1) if OPTS.netlist_only:
return (b.height, 1)
# Search for the smallest multiple that works # Search for the smallest multiple that works
cell_multiple = 1 cell_multiple = 1
while cell_multiple < 3: while cell_multiple < 5:
cell_height = cell_multiple * b.height cell_height = cell_multiple * b.height
and3 = factory.create(module_type="pand3", # debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height))
height=cell_height) try:
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) and3 = factory.create(module_type="pand3",
if drc_errors + lvs_errors == 0: height=cell_height)
return (cell_height, cell_multiple) except drc_error:
# debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height))
pass
else:
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True)
total_errors = drc_errors + lvs_errors
if total_errors == 0:
debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple))
return (cell_height, cell_multiple)
cell_multiple += 1 cell_multiple += 1
else: else:
debug.error("Couldn't find a valid decoder height multiple.", -1) debug.error("Couldn't find a valid decoder height multiple.", -1)
@ -63,8 +90,8 @@ class hierarchical_decoder(design.design):
self.setup_layout_constants() self.setup_layout_constants()
self.place_pre_decoder() self.place_pre_decoder()
self.place_row_decoder() self.place_row_decoder()
self.route_input_rails() self.route_inputs()
self.route_predecode_rails() self.route_decoder_bus()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_all_coordinates() self.offset_all_coordinates()
self.add_boundary() self.add_boundary()
@ -118,7 +145,7 @@ class hierarchical_decoder(design.design):
def setup_netlist_constants(self): def setup_netlist_constants(self):
self.predec_groups = [] # This array is a 2D array. self.predec_groups = [] # This array is a 2D array.
# Distributing vertical rails to different groups. One group belongs to one pre-decoder. # Distributing vertical bus to different groups. One group belongs to one pre-decoder.
# For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will # For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will
# have total 16 output lines out of these 3 pre-decoders and they will # have total 16 output lines out of these 3 pre-decoders and they will
# be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ] # be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
@ -157,39 +184,46 @@ class hierarchical_decoder(design.design):
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
# We may have more than one bitcell per decoder row
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
# We will place this many final decoders per row
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
# Calculates height and width of row-decoder # Calculates height and width of row-decoder
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
nand_width = self.and2.width nand_width = self.and2.width
else: else:
nand_width = self.and3.width nand_width = self.and3.width
self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1)
self.row_decoder_height = self.inv.height * self.rows self.row_decoder_height = self.inv.height * self.num_rows
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
# Calculates height and width of hierarchical decoder # Calculates height and width of hierarchical decoder
self.height = self.row_decoder_height # Add extra pitch for good measure
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch
self.width = self.input_routing_width + self.predecoder_width \ self.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width + nand_width + self.inv.width + self.internal_routing_width \
+ self.decoders_per_row * nand_width + self.inv.width
def route_input_rails(self): def route_inputs(self):
""" Create input rails for the predecoders """ """ Create input bus for the predecoders """
# inputs should be as high as the decoders # inputs should be as high as the decoders
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
# Find the left-most predecoder # Find the left-most predecoder
min_x = 0 min_x = 0
if self.no_of_pre2x4 > 0: if self.no_of_pre2x4 > 0:
min_x = min(min_x, -self.pre2_4.width) min_x = min(min_x, self.pre2x4_inst[0].lx())
if self.no_of_pre3x8 > 0: if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width) min_x = min(min_x, self.pre3x8_inst[0].lx())
input_offset=vector(min_x - self.input_routing_width, 0) input_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="m2", self.input_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=input_offset, offset=input_offset,
names=input_bus_names, names=input_bus_names,
length=input_height) length=input_height)
self.route_input_to_predecodes() self.route_input_to_predecodes()
@ -199,7 +233,7 @@ class hierarchical_decoder(design.design):
for i in range(2): for i in range(2):
index = pre_num * 2 + i index = pre_num * 2 + i
input_pos = self.input_rails["addr_{}".format(index)] input_pos = self.input_bus["addr_{}".format(index)]
in_name = "in_{}".format(i) in_name = "in_{}".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
@ -209,13 +243,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
for i in range(3): for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2 index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pos = self.input_rails["addr_{}".format(index)] input_pos = self.input_bus["addr_{}".format(index)]
in_name = "in_{}".format(i) in_name = "in_{}".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
@ -225,10 +259,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_rail(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
def route_input_rail(self, input_offset, output_offset): def route_input_bus(self, input_offset, output_offset):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ """
Route a vertical M2 coordinate to another
vertical M2 coordinate to the predecode inputs
"""
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=input_offset) offset=input_offset)
@ -242,7 +279,7 @@ class hierarchical_decoder(design.design):
for i in range(self.num_inputs): for i in range(self.num_inputs):
self.add_pin("addr_{0}".format(i), "INPUT") self.add_pin("addr_{0}".format(i), "INPUT")
for j in range(self.rows): for j in range(self.num_outputs):
self.add_pin("decode_{0}".format(j), "OUTPUT") self.add_pin("decode_{0}".format(j), "OUTPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
@ -311,18 +348,17 @@ class hierarchical_decoder(design.design):
else: else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height) base= vector(-self.pre2_4.width, num * self.pre2_4.height)
self.pre2x4_inst[num].place(base) self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
def place_pre3x8(self, num): def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3): if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width, 0) offset = vector(-self.pre_3_8.width, 0)
mirror = "R0"
else: else:
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
offset = vector(-self.pre3_8.width, height) offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset) self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
def create_row_decoder(self): def create_row_decoder(self):
""" Create the row-decoder by placing AND2/AND3 and Inverters """ Create the row-decoder by placing AND2/AND3 and Inverters
@ -339,14 +375,14 @@ class hierarchical_decoder(design.design):
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
row = len(self.predec_groups[0]) * j + i output = len(self.predec_groups[0]) * j + i
if (row < self.rows): if (output < self.num_outputs):
name = self.AND_FORMAT.format(row) name = self.AND_FORMAT.format(output)
self.and_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.and2)) mod=self.and2))
pins =["out_{0}".format(i), pins =["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(j + len(self.predec_groups[0])),
"decode_{0}".format(row), "decode_{0}".format(output),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) self.connect_inst(pins)
@ -355,18 +391,18 @@ class hierarchical_decoder(design.design):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])): for k in range(len(self.predec_groups[2])):
row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ output = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0]) * j + i + len(self.predec_groups[0]) * j + i
if (row < self.rows): if (output < self.num_outputs):
name = self.AND_FORMAT.format(row) name = self.AND_FORMAT.format(output)
self.and_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.and3)) mod=self.and3))
pins = ["out_{0}".format(i), pins = ["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(j + len(self.predec_groups[0])),
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
"decode_{0}".format(row), "decode_{0}".format(output),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) self.connect_inst(pins)
@ -380,7 +416,10 @@ class hierarchical_decoder(design.design):
self.route_decoder() self.route_decoder()
def place_decoder_and_array(self): def place_decoder_and_array(self):
""" Add a column of AND gates for final decode """ """
Add a column of AND gates for final decode.
This may have more than one decoder per row to match the bitcell height.
"""
# Row Decoder AND GATE array for address inputs <5. # Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
@ -392,9 +431,13 @@ class hierarchical_decoder(design.design):
self.place_and_array(and_mod=self.and3) self.place_and_array(and_mod=self.and3)
def place_and_array(self, and_mod): def place_and_array(self, and_mod):
""" Add a column of AND gates for the decoder above the predecoders.""" """
Add a column of AND gates for the decoder above the predecoders.
for row in range(self.rows): """
for inst_index in range(self.num_outputs):
row = math.floor(inst_index / self.decoders_per_row)
dec = inst_index % self.decoders_per_row
if ((row % 2) == 0): if ((row % 2) == 0):
y_off = and_mod.height * row y_off = and_mod.height * row
mirror = "R0" mirror = "R0"
@ -402,46 +445,52 @@ class hierarchical_decoder(design.design):
y_off = and_mod.height * (row + 1) y_off = and_mod.height * (row + 1)
mirror = "MX" mirror = "MX"
self.and_inst[row].place(offset=[self.internal_routing_width, y_off], x_off = self.internal_routing_width + dec * and_mod.width
mirror=mirror) self.and_inst[inst_index].place(offset=vector(x_off, y_off),
mirror=mirror)
def route_decoder(self): def route_decoder(self):
""" Add the pins. """ """ Add the pins. """
for row in range(self.rows): for output in range(self.num_outputs):
z_pin = self.and_inst[row].get_pin("Z") z_pin = self.and_inst[output].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(row), self.add_layout_pin(text="decode_{0}".format(output),
layer="m1", layer="m1",
offset=z_pin.ll(), offset=z_pin.ll(),
width=z_pin.width(), width=z_pin.width(),
height=z_pin.height()) height=z_pin.height())
def route_predecode_rails(self): def route_decoder_bus(self):
""" Creates vertical metal 2 rails to connect predecoder and decoder stages.""" """
Creates vertical metal 2 bus to connect predecoder and decoder stages.
"""
# This is not needed for inputs <4 since they have no pre/decode stages. # This is not needed for inputs <4 since they have no pre/decode stages.
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
input_offset = vector(0.5 * self.m2_width, 0) # This leaves an offset for the predecoder output jogs
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)] input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
self.predecode_rails = self.create_vertical_pin_bus(layer="m2", self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=input_offset, offset=vector(0, 0),
names=input_bus_names, names=input_bus_names,
length=self.height) length=self.height)
self.route_rails_to_predecodes() self.route_predecodes_to_bus()
self.route_rails_to_decoder() self.route_bus_to_decoder()
def route_rails_to_predecodes(self):
""" Iterates through all of the predecodes and connects to the rails including the offsets """
def route_predecodes_to_bus(self):
"""
Iterates through all of the predecodes
and connects to the rails including the offsets
"""
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre2x4): for pre_num in range(self.no_of_pre2x4):
for i in range(4): for i in range(4):
predecode_name = "predecode_{}".format(pre_num * 4 + i) predecode_name = "predecode_{}".format(pre_num * 4 + i)
out_name = "out_{}".format(i) out_name = "out_{}".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name) pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin) x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
@ -449,52 +498,82 @@ class hierarchical_decoder(design.design):
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
out_name = "out_{}".format(i) out_name = "out_{}".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name) pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin) x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
def route_rails_to_decoder(self): def route_bus_to_decoder(self):
""" Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of AND2/AND3 gates come from different groups.
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
[8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
[0,4,8] and second AND3 is connected to [0,4,9] ........... and the
128th AND3 is connected to [3,7,15]
""" """
row_index = 0 Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of AND2/AND3 gates come from different groups.
For example for these groups
[ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
the first AND3 inputs are connected to [0,4,8],
second AND3 is connected to [0,4,9],
...
and the 128th AND3 is connected to [3,7,15]
"""
output_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (row_index < self.rows): if (output_index < self.num_outputs):
row_index = math.floor(output_index / self.decoders_per_row)
row_remainder = (output_index % self.decoders_per_row)
row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) self.route_predecode_bus_outputs(predecode_name,
row_index = row_index + 1 self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch)
output_index = output_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
for index_C in self.predec_groups[2]: for index_C in self.predec_groups[2]:
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (row_index < self.rows): if (output_index < self.num_outputs):
row_index = math.floor(output_index / self.decoders_per_row)
row_remainder = (output_index % self.decoders_per_row)
row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m3_pitch
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A")) self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch)
predecode_name = "predecode_{}".format(index_C) predecode_name = "predecode_{}".format(index_C)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C")) self.route_predecode_bus_outputs(predecode_name,
row_index = row_index + 1 self.and_inst[output_index].get_pin("C"),
row_offset + 2 * self.m3_pitch)
output_index = output_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """ """
Add a pin for each row of vdd/gnd which are
must-connects next level up.
"""
# The vias will be placed in the center and right of the cells, respectively. # The vias will be placed at the right of the cells.
xoffset = self.and_inst[0].rx() xoffset = max(x.rx() for x in self.and_inst)
for num in range(0, self.rows): for num in range(0, self.num_outputs):
# Only add the power pin for the 1st in each row
if num % self.decoders_per_row:
continue
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
# The nand and inv are the same height rows... # The nand and inv are the same height rows...
supply_pin = self.and_inst[num].get_pin(pin_name) supply_pin = self.and_inst[num].get_pin(pin_name)
pin_pos = vector(xoffset, supply_pin.cy()) pin_pos = vector(xoffset, supply_pin.cy())
self.add_path("m1",
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
self.add_power_pin(name=pin_name, self.add_power_pin(name=pin_name,
loc=pin_pos) loc=pin_pos)
@ -503,23 +582,42 @@ class hierarchical_decoder(design.design):
self.copy_layout_pin(pre, "vdd") self.copy_layout_pin(pre, "vdd")
self.copy_layout_pin(pre, "gnd") self.copy_layout_pin(pre, "gnd")
def route_predecode_rail(self, rail_name, pin): def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
""" Connect the routing rail to the given metal1 pin """ """
rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y) Connect the routing rail to the given metal1 pin
self.add_path("m1", [rail_pos, pin.lc()]) using a routing track at the given y_offset
self.add_via_center(layers=self.m1_stack,
offset=rail_pos) """
pin_pos = pin.center()
# If we have a single decoder per row, we can route on M1
if self.decoders_per_row == 1:
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
# If not, we must route over the decoder cells on M3
else:
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
mid_pos = vector(pin_pos.x, rail_pos.y)
self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos)
self.add_via_center(layers=self.m1_stack,
offset=pin_pos)
def route_predecode_rail_m3(self, rail_name, pin): def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
""" Connect the routing rail to the given metal1 pin """ """
Connect the routing rail to the given metal1 pin using a jog
to the right of the cell at the given x_offset.
"""
# This routes the pin up to the rail, basically, to avoid conflicts. # This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router. # It would be fixed with a channel router.
mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2) pin_pos = pin.center()
rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y) mid_point1 = vector(x_offset, pin_pos.y)
mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
self.add_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pin.center())
self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos) offset=rail_pos)
def input_load(self): def input_load(self):

View File

@ -90,17 +90,14 @@ class port_address(design.design):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc()
driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1)
self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos])
def add_modules(self): def add_modules(self):
self.row_decoder = factory.create(module_type="decoder", self.row_decoder = factory.create(module_type="decoder",
rows=self.num_rows) num_outputs=self.num_rows)
self.add_mod(self.row_decoder) self.add_mod(self.row_decoder)
self.wordline_driver = factory.create(module_type="wordline_driver", self.wordline_driver = factory.create(module_type="wordline_driver",
@ -108,11 +105,10 @@ class port_address(design.design):
cols=self.num_cols) cols=self.num_cols)
self.add_mod(self.wordline_driver) self.add_mod(self.wordline_driver)
def create_row_decoder(self): def create_row_decoder(self):
""" Create the hierarchical row decoder """ """ Create the hierarchical row decoder """
self.row_decoder_inst = self.add_inst(name="row_decoder", self.row_decoder_inst = self.add_inst(name="row_decoder",
mod=self.row_decoder) mod=self.row_decoder)
temp = [] temp = []

View File

@ -15,6 +15,7 @@ from globals import OPTS
from utils import round_to_grid from utils import round_to_grid
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
from errors import drc_error
class pinv(pgate.pgate): class pinv(pgate.pgate):
@ -108,11 +109,14 @@ class pinv(pgate.pgate):
# This is a poly-to-poly of a flipped cell # This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space, self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space) self.poly_extend_active + self.poly_space)
total_height = tx_height + min_channel + 2 * self.top_bottom_space
debug.check(self.height > total_height, total_height = tx_height + min_channel + 2 * self.top_bottom_space
"Cell height {0} too small for simple min height {1}.".format(self.height, # debug.check(self.height > total_height,
total_height)) # "Cell height {0} too small for simple min height {1}.".format(self.height,
# total_height))
if total_height > self.height:
msg = "Cell height {0} too small for simple min height {1}.".format(self.height, total_height)
raise drc_error(msg)
# Determine the height left to the transistors to determine # Determine the height left to the transistors to determine
# the number of fingers # the number of fingers
@ -144,12 +148,16 @@ class pinv(pgate.pgate):
# with LVS property mismatch errors when fingers are not a grid # with LVS property mismatch errors when fingers are not a grid
# length and get rounded in the offset geometry. # length and get rounded in the offset geometry.
self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults)
debug.check(self.nmos_width >= drc("minwidth_tx"), # debug.check(self.nmos_width >= drc("minwidth_tx"),
"Cannot finger NMOS transistors to fit cell height.") # "Cannot finger NMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) if self.nmos_width < drc("minwidth_tx"):
debug.check(self.pmos_width >= drc("minwidth_tx"), raise drc_error("Cannot finger NMOS transistors to fit cell height.")
"Cannot finger PMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults)
#debug.check(self.pmos_width >= drc("minwidth_tx"),
# "Cannot finger PMOS transistors to fit cell height.")
if self.pmos_width < drc("minwidth_tx"):
raise drc_error("Cannot finger NMOS transistors to fit cell height.")
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """

View File

@ -98,7 +98,7 @@ class pnand2(pgate.pgate):
# metal spacing to allow contacts on any layer # metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width, self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width, self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width, self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width) self.m3_space + contact.m2_via.second_layer_width)
@ -173,13 +173,15 @@ class pnand2(pgate.pgate):
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height)) 0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height))
def add_well_contacts(self): def add_well_contacts(self):
""" """
Add n/p well taps to the layout and connect to supplies Add n/p well taps to the layout and connect to supplies
AFTER the wells are created AFTER the wells are created
""" """
self.add_nwell_contact(self.pmos, self.pmos2_pos) self.add_nwell_contact(self.pmos,
self.add_pwell_contact(self.nmos_nd, self.nmos2_pos) self.pmos2_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_nd,
self.nmos2_pos + vector(self.m1_pitch, 0))
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
@ -197,7 +199,7 @@ class pnand2(pgate.pgate):
self.nmos2_inst, self.nmos2_inst,
inputB_yoffset, inputB_yoffset,
"B", "B",
position="right") position="center")
# This will help with the wells and the input/output placement # This will help with the wells and the input/output placement
self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \ self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
@ -209,6 +211,7 @@ class pnand2(pgate.pgate):
def route_output(self): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D") pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center() top_pin_offset = pmos_pin.center()
@ -217,29 +220,46 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center() bottom_pin_offset = nmos_pin.center()
# Output pin # Output pin
out_offset = vector(nmos_pin.center().x + self.m1_pitch, c_pin = self.get_pin("B")
out_offset = vector(c_pin.cx() + self.m1_pitch,
self.inputA_yoffset) self.inputA_yoffset)
# Midpoints of the L routes go horizontal first then vertical # This routes on M2
mid1_offset = vector(out_offset.x, top_pin_offset.y) # # Midpoints of the L routes go horizontal first then vertical
# mid1_offset = vector(out_offset.x, top_pin_offset.y)
# mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
# # Non-preferred active contacts
# self.add_via_center(layers=self.m1_stack,
# directions=("V", "H"),
# offset=pmos_pin.center())
# # Non-preferred active contacts
# self.add_via_center(layers=self.m1_stack,
# directions=("V", "H"),
# offset=nmos_pin.center())
# self.add_via_center(layers=self.m1_stack,
# offset=out_offset)
# # PMOS1 to mid-drain to NMOS2 drain
# self.add_path("m2",
# [top_pin_offset, mid1_offset, out_offset,
# mid2_offset, bottom_pin_offset])
# This routes on M1
# Midpoints of the L routes goes vertical first then horizontal
mid1_offset = vector(top_pin_offset.x, out_offset.y)
# Midpoints of the L routes goes horizontal first then vertical
mid2_offset = vector(out_offset.x, bottom_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
# Non-preferred active contacts self.add_path("m1",
self.add_via_center(layers=self.m1_stack, [top_pin_offset, mid1_offset, out_offset])
directions=("V", "H"), # Route in two segments to have the width rule
offset=pmos_pin.center()) self.add_path("m1",
# Non-preferred active contacts [bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)],
self.add_via_center(layers=self.m1_stack, width=nmos_pin.height())
directions=("V", "H"), self.add_path("m1",
offset=nmos_pin.center()) [mid2_offset, out_offset])
self.add_via_center(layers=self.m1_stack,
offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("m2",
[top_pin_offset, mid1_offset, out_offset,
mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="m1", layer="m1",

View File

@ -12,6 +12,7 @@ from tech import drc, parameter, spice
from vector import vector from vector import vector
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
from globals import OPTS
class pnand3(pgate.pgate): class pnand3(pgate.pgate):
@ -190,8 +191,10 @@ class pnand3(pgate.pgate):
def add_well_contacts(self): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos, self.pmos3_pos) self.add_nwell_contact(self.pmos,
self.add_pwell_contact(self.nmos_ns, self.nmos3_pos) self.pmos3_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_ns,
self.nmos3_pos + vector(self.m1_pitch, 0))
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
@ -220,18 +223,22 @@ class pnand3(pgate.pgate):
self.nmos3_inst, self.nmos3_inst,
self.inputC_yoffset, self.inputC_yoffset,
"C", "C",
position="center") position="right")
# FIXME: constant hack # FIXME: constant hack
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch if OPTS.tech_name == "s8":
self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch
else:
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch
self.route_input_gate(self.pmos1_inst, self.route_input_gate(self.pmos1_inst,
self.nmos1_inst, self.nmos1_inst,
self.inputA_yoffset, self.inputA_yoffset,
"A", "A",
position="center") position="left")
def route_output(self): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D") pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain # PMOS3 drain
@ -239,29 +246,56 @@ class pnand3(pgate.pgate):
# NMOS3 drain # NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D") nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins # midpoint for routing
self.add_via_center(layers=self.m1_stack, mid_offset = vector(nmos3_pin.cx() + self.m1_pitch,
offset=pmos1_pin.center(), self.inputA_yoffset)
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack, # Aligned with the well taps
offset=pmos3_pin.center(), out_offset = vector(self.nwell_contact.cx(),
directions=("V", "V")) self.inputA_yoffset)
self.add_via_center(layers=self.m1_stack,
offset=nmos3_pin.center(),
directions=("V", "V"))
# PMOS3 and NMOS3 are drain aligned # Go up to metal2 for ease on all output pins
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()]) # self.add_via_center(layers=self.m1_stack,
# Route in the A input track (top track) # offset=pmos1_pin.center(),
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) # directions=("V", "V"))
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) # self.add_via_center(layers=self.m1_stack,
# offset=pmos3_pin.center(),
# directions=("V", "V"))
# self.add_via_center(layers=self.m1_stack,
# offset=nmos3_pin.center(),
# directions=("V", "V"))
# # Route in the A input track (top track)
# mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
# self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_via_center(layers=self.m1_stack, # self.add_via_center(layers=self.m1_stack,
offset=mid_offset) # offset=mid_offset)
top_left_pin_offset = pmos1_pin.center()
top_right_pin_offset = pmos3_pin.center()
bottom_pin_offset = nmos3_pin.center()
# PMOS1 to output
self.add_path("m1", [top_left_pin_offset,
vector(top_left_pin_offset.x, out_offset.y),
out_offset])
# PMOS3 to output
self.add_path("m1", [top_right_pin_offset,
vector(top_right_pin_offset.x, mid_offset.y),
mid_offset])
# NMOS3 to output
mid2_offset = vector(mid_offset.x, bottom_pin_offset.y)
self.add_path("m1",
[bottom_pin_offset, mid2_offset],
width=nmos3_pin.height())
mid3_offset = vector(mid_offset.x, nmos3_pin.by())
self.add_path("m1", [mid3_offset, mid_offset])
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="m1", layer="m1",
offset=mid_offset, offset=out_offset,
width=contact.m1_via.first_layer_width, width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height) height=contact.m1_via.first_layer_height)

View File

@ -107,10 +107,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 == "s8":
if OPTS.tech_name == None:
print("here {0}".format(self.name))
# s8 technology is in microns # s8 technology is in microns
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],
self.mults, self.mults,
@ -474,13 +471,13 @@ class ptx(design.design):
"""Returns an object representing the parameters for delay in tau units.""" """Returns an object representing the parameters for delay in tau units."""
# FIXME: Using the same definition as the pinv.py. # FIXME: Using the same definition as the pinv.py.
parasitic_delay = 1 parasitic_delay = 1
size = self.mults*self.tx_width/drc("minwidth_tx") size = self.mults * self.tx_width / drc("minwidth_tx")
return logical_effort.logical_effort(self.name, return logical_effort.logical_effort(self.name,
size, size,
self.input_load(), self.input_load(),
cout, cout,
parasitic_delay) parasitic_delay)
def input_load(self): def input_load(self):
""" """
@ -488,7 +485,7 @@ class ptx(design.design):
""" """
# FIXME: this will be applied for the loads of the drain/source # FIXME: this will be applied for the loads of the drain/source
return self.mults*self.tx_width/drc("minwidth_tx") return self.mults * self.tx_width / drc("minwidth_tx")
def add_diff_contact(self, label, pos): def add_diff_contact(self, label, pos):
contact=self.add_via_center(layers=self.active_stack, contact=self.add_via_center(layers=self.active_stack,
@ -499,14 +496,25 @@ class ptx(design.design):
well_type=self.well_type) well_type=self.well_type)
if hasattr(self, "li_stack"): if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack, contact=self.add_via_center(layers=self.li_stack,
offset=pos) offset=pos,
directions=("V", "V"))
# contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height
# min_area = drc("minarea_m1")
# width = contact.mod.second_layer_width
# if contact_area < min_area:
# height = min_area / width
# else:
# height = contact.mod.second_layer_height
width = contact.mod.second_layer_width
height = contact.mod.second_layer_height
self.add_layout_pin_rect_center(text=label, self.add_layout_pin_rect_center(text=label,
layer="m1", layer="m1",
offset=pos, offset=pos,
width=contact.mod.second_layer_width, width=width,
height=contact.mod.second_layer_height) height=height)
return(contact) return(contact)
def get_cin(self): def get_cin(self):

View File

@ -28,39 +28,39 @@ class hierarchical_decoder_test(openram_test):
factory.reset() factory.reset()
debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=16) a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=17) a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=23) a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=32) a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=65) a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=128) a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=341) a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=512) a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -20,47 +20,38 @@ class hierarchical_decoder_test(openram_test):
def runTest(self): def runTest(self):
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)
# Doesn't require hierarchical decoder
# debug.info(1, "Testing 4 row sample for hierarchical_decoder")
# a = hierarchical_decoder.hierarchical_decoder(name="hd1, rows=4)
# self.local_check(a)
# Doesn't require hierarchical decoder
# debug.info(1, "Testing 8 row sample for hierarchical_decoder")
# a = hierarchical_decoder.hierarchical_decoder(name="hd2", rows=8)
# self.local_check(a)
# check hierarchical decoder for single port # check hierarchical decoder for single port
debug.info(1, "Testing 16 row sample for hierarchical_decoder") debug.info(1, "Testing 16 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=16) a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 17 row sample for hierarchical_decoder") debug.info(1, "Testing 17 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=17) a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 23 row sample for hierarchical_decoder") debug.info(1, "Testing 23 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=23) a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder") debug.info(1, "Testing 32 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=32) a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 65 row sample for hierarchical_decoder") debug.info(1, "Testing 65 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=65) a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder") debug.info(1, "Testing 128 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=128) a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 341 row sample for hierarchical_decoder") debug.info(1, "Testing 341 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=341) a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder") debug.info(1, "Testing 512 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=512) a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -51,7 +51,7 @@ class openram_back_end_test(openram_test):
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
exe_name = "{0}/openram.py ".format(OPENRAM_HOME) exe_name = "{0}/openram.py ".format(OPENRAM_HOME)
else: else:
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file, out_file,
@ -64,33 +64,32 @@ class openram_back_end_test(openram_test):
# assert an error until we actually check a resul # assert an error until we actually check a resul
for extension in ["gds", "v", "lef", "sp"]: for extension in ["gds", "v", "lef", "sp"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension) filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
debug.info(1,"Checking for file: " + filename) debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename),True) self.assertEqual(os.path.exists(filename), True)
# Make sure there is any .lib file # Make sure there is any .lib file
import glob import glob
files = glob.glob('{0}/*.lib'.format(out_path)) files = glob.glob('{0}/*.lib'.format(out_path))
self.assertTrue(len(files)>0) self.assertTrue(len(files)>0)
# Make sure there is any .html file # Make sure there is any .html file
if os.path.exists(out_path): if os.path.exists(out_path):
datasheets = glob.glob('{0}/*html'.format(out_path)) datasheets = glob.glob('{0}/*html'.format(out_path))
self.assertTrue(len(datasheets)>0) self.assertTrue(len(datasheets)>0)
# grep any errors from the output # grep any errors from the output
output_log = open("{0}/output.log".format(out_path),"r") output_log = open("{0}/output.log".format(out_path), "r")
output = output_log.read() output = output_log.read()
output_log.close() output_log.close()
self.assertEqual(len(re.findall('ERROR',output)),0) self.assertEqual(len(re.findall('ERROR', output)), 0)
self.assertEqual(len(re.findall('WARNING',output)),0) self.assertEqual(len(re.findall('WARNING', output)), 0)
# now clean up the directory # now clean up the directory
if OPTS.purge_temp: if OPTS.purge_temp:
if os.path.exists(out_path): if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True) shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False) self.assertEqual(os.path.exists(out_path), False)
globals.end_openram() globals.end_openram()

View File

@ -51,7 +51,7 @@ class openram_front_end_test(openram_test):
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
exe_name = "{0}/openram.py ".format(OPENRAM_HOME) exe_name = "{0}/openram.py ".format(OPENRAM_HOME)
else: else:
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
config_name = "{0}/tests/configs/config_front_end.py".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_front_end.py".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file, out_file,
@ -64,33 +64,32 @@ class openram_front_end_test(openram_test):
# assert an error until we actually check a result # assert an error until we actually check a result
for extension in ["v", "lef", "sp", "gds"]: for extension in ["v", "lef", "sp", "gds"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension) filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
debug.info(1,"Checking for file: " + filename) debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename),True) self.assertEqual(os.path.exists(filename), True)
# Make sure there is any .lib file # Make sure there is any .lib file
import glob import glob
files = glob.glob('{0}/*.lib'.format(out_path)) files = glob.glob('{0}/*.lib'.format(out_path))
self.assertTrue(len(files)>0) self.assertTrue(len(files)>0)
# Make sure there is any .html file # Make sure there is any .html file
if os.path.exists(out_path): if os.path.exists(out_path):
datasheets = glob.glob('{0}/*html'.format(out_path)) datasheets = glob.glob('{0}/*html'.format(out_path))
self.assertTrue(len(datasheets)>0) self.assertTrue(len(datasheets)>0)
# grep any errors from the output # grep any errors from the output
output_log = open("{0}/output.log".format(out_path),"r") output_log = open("{0}/output.log".format(out_path), "r")
output = output_log.read() output = output_log.read()
output_log.close() output_log.close()
self.assertEqual(len(re.findall('ERROR',output)),0) self.assertEqual(len(re.findall('ERROR', output)), 0)
self.assertEqual(len(re.findall('WARNING',output)),0) self.assertEqual(len(re.findall('WARNING', output)), 0)
# now clean up the directory
# now clean up the directory
if OPTS.purge_temp: if OPTS.purge_temp:
if os.path.exists(out_path): if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True) shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False) self.assertEqual(os.path.exists(out_path), False)
globals.end_openram() globals.end_openram()