Add place_inst routine.

Separate create netlist and layout in some modules.
This commit is contained in:
Matt Guthaus 2018-08-27 10:42:40 -07:00
parent 8c73a26daa
commit 138a70fc23
10 changed files with 403 additions and 255 deletions

View File

@ -49,6 +49,10 @@ class geometry:
ll = vector(min(first[0],second[0]),min(first[1],second[1])) ll = vector(min(first[0],second[0]),min(first[1],second[1]))
ur = vector(max(first[0],second[0]),max(first[1],second[1])) ur = vector(max(first[0],second[0]),max(first[1],second[1]))
self.boundary=[ll,ur] self.boundary=[ll,ur]
def update_boundary(self):
""" Update the boundary with a new placement. """
self.compute_boundary(self.offset,self.mirror,self.rotate)
def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0): def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0):
""" Transform with offset, mirror and rotation to get the absolute pin location. """ Transform with offset, mirror and rotation to get the absolute pin location.
@ -124,7 +128,7 @@ class instance(geometry):
An instance of an instance/module with a specified location and An instance of an instance/module with a specified location and
rotation rotation
""" """
def __init__(self, name, mod, offset, mirror, rotate): 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) geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.") debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")

View File

@ -118,6 +118,15 @@ class layout(lef.lef):
for pin in pin_list: for pin in pin_list:
pin.rect = [pin.ll() - offset, pin.ur() - offset] pin.rect = [pin.ll() - offset, pin.ur() - offset]
def place_inst(self, name, offset, mirror="R0", rotate=0):
""" This updates the placement of an instance. """
inst = self.get_inst(name)
debug.info(3, "placing instance {}".format(inst))
# Update the placement of an already added instance
inst.offset = offset
inst.mirror = mirror
inst.rotate = rotate
inst.update_boundary()
def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0): def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
"""Adds an instance of a mod to this module""" """Adds an instance of a mod to this module"""

View File

@ -91,19 +91,26 @@ class spice(verilog.verilog):
group of modules are generated.""" group of modules are generated."""
if (check and (len(self.insts[-1].mod.pins) != len(args))): if (check and (len(self.insts[-1].mod.pins) != len(args))):
debug.error("Connections: {}".format(self.insts[-1].mod.pins)) import pprint
debug.error("Connections: {}".format(args)) modpins_string=pprint.pformat(self.insts[-1].mod.pins)
argpins_string=pprint.pformat(args)
debug.error("Connections: {}".format(modpins_string))
debug.error("Connections: {}".format(argpins_string))
debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins), debug.error("Number of net connections ({0}) does not match last instance ({1})".format(len(self.insts[-1].mod.pins),
len(args)), 1) len(args)), 1)
self.conns.append(args) self.conns.append(args)
if check and (len(self.insts)!=len(self.conns)): if check and (len(self.insts)!=len(self.conns)):
import pprint
insts_string=pprint.pformat(self.insts)
conns_string=pprint.pformat(self.conns)
debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name, debug.error("{0} : Not all instance pins ({1}) are connected ({2}).".format(self.name,
len(self.insts), len(self.insts),
len(self.conns))) len(self.conns)))
debug.error("Instances: \n"+str(self.insts)) debug.error("Instances: \n"+str(insts_string))
debug.error("-----") debug.error("-----")
debug.error("Connections: \n"+str(self.conns),1) debug.error("Connections: \n"+str(conns_string),1)

View File

@ -31,15 +31,14 @@ class bitcell_array(design.design):
self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width self.height = self.row_size*self.cell.height + drc["well_enclosure_active"] + self.m1_width
self.width = self.column_size*self.cell.width + self.m1_width self.width = self.column_size*self.cell.width + self.m1_width
self.add_pins() self.create_netlist()
self.create_layout() self.create_layout()
self.add_layout_pins()
# 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()
self.DRC_LVS()
def add_pins(self): def add_pins(self):
row_list = self.cell.list_all_wl_names() row_list = self.cell.list_all_wl_names()
@ -55,7 +54,6 @@ class bitcell_array(design.design):
def create_layout(self): def create_layout(self):
xoffset = 0.0 xoffset = 0.0
self.cell_inst = {}
for col in range(self.column_size): for col in range(self.column_size):
yoffset = 0.0 yoffset = 0.0
for row in range(self.row_size): for row in range(self.row_size):
@ -68,16 +66,29 @@ class bitcell_array(design.design):
tempy = yoffset tempy = yoffset
dir_key = "" dir_key = ""
self.cell_inst[row,col]=self.add_inst(name=name, self.place_inst(name=name,
mod=self.cell, offset=[xoffset, tempy],
offset=[xoffset, tempy], mirror=dir_key)
mirror=dir_key)
self.connect_inst(self.cell.list_bitcell_pins(col, row))
yoffset += self.cell.height yoffset += self.cell.height
xoffset += self.cell.width xoffset += self.cell.width
self.add_layout_pins()
self.DRC_LVS()
def create_netlist(self):
""" Create and connect the netlist """
self.add_pins()
self.cell_inst = {}
for col in range(self.column_size):
for row in range(self.row_size):
name = "bit_r{0}_c{1}".format(row, col)
self.cell_inst[row,col]=self.add_inst(name=name,
mod=self.cell)
self.connect_inst(self.cell.list_bitcell_pins(col, row))
def add_layout_pins(self): def add_layout_pins(self):
""" Add the layout pins """ """ Add the layout pins """

View File

@ -29,11 +29,15 @@ class dff_array(design.design):
self.width = self.columns * self.dff.width self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height self.height = self.rows * self.dff.height
self.create_netlist()
self.create_layout() self.create_layout()
def create_layout(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_dff_array() self.create_dff_array()
def create_layout(self):
self.place_dff_array()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
@ -53,22 +57,28 @@ class dff_array(design.design):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[row,col]=self.add_inst(name=name, self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff, mod=self.dff)
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(row,col), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col), self.get_dout_name(row,col),
"clk", "clk",
"vdd", "vdd",
"gnd"]) "gnd"])
def place_dff_array(self):
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.place_inst(name=name,
offset=base,
mirror=mirror)
def get_din_name(self, row, col): def get_din_name(self, row, col):
if self.columns == 1: if self.columns == 1:
din_name = "din[{0}]".format(row) din_name = "din[{0}]".format(row)

View File

@ -27,11 +27,15 @@ class dff_buf_array(design.design):
self.width = self.columns * self.dff.width self.width = self.columns * self.dff.width
self.height = self.rows * self.dff.height self.height = self.rows * self.dff.height
self.create_netlist()
self.create_layout() self.create_layout()
def create_layout(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.create_dff_array() self.create_dff_array()
def create_layout(self):
self.place_dff_array()
self.add_layout_pins() self.add_layout_pins()
self.DRC_LVS() self.DRC_LVS()
@ -52,16 +56,8 @@ class dff_buf_array(design.design):
for row in range(self.rows): for row in range(self.rows):
for col in range(self.columns): for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col) name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.dff_insts[row,col]=self.add_inst(name=name, self.dff_insts[row,col]=self.add_inst(name=name,
mod=self.dff, mod=self.dff)
offset=base,
mirror=mirror)
self.connect_inst([self.get_din_name(row,col), self.connect_inst([self.get_din_name(row,col),
self.get_dout_name(row,col), self.get_dout_name(row,col),
self.get_dout_bar_name(row,col), self.get_dout_bar_name(row,col),
@ -69,6 +65,20 @@ class dff_buf_array(design.design):
"vdd", "vdd",
"gnd"]) "gnd"])
def place_dff_array(self):
for row in range(self.rows):
for col in range(self.columns):
name = "Xdff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"
else:
base = vector(col*self.dff.width,(row+1)*self.dff.height)
mirror = "MX"
self.place_inst(name=name,
offset=base,
mirror=mirror)
def get_din_name(self, row, col): def get_din_name(self, row, col):
if self.columns == 1: if self.columns == 1:
din_name = "din[{0}]".format(row) din_name = "din[{0}]".format(row)

View File

@ -26,6 +26,9 @@ class hierarchical_decoder(design.design):
self.mod_bitcell = getattr(c, OPTS.bitcell) self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell_height = self.mod_bitcell.height self.bitcell_height = self.mod_bitcell.height
self.NAND_FORMAT = "DEC_NAND[{0}]"
self.INV_FORMAT = "DEC_INV_[{0}]"
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
@ -33,22 +36,25 @@ class hierarchical_decoder(design.design):
self.num_inputs = int(math.log(self.rows, 2)) self.num_inputs = int(math.log(self.rows, 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_layout() self.create_layout()
self.offset_all_coordinates()
self.DRC_LVS()
def create_layout(self): def create_netlist(self):
self.add_modules() self.add_modules()
self.setup_layout_constants() self.setup_netlist_constants()
self.add_pins() self.add_pins()
self.create_pre_decoder() self.create_pre_decoder()
self.create_row_decoder() self.create_row_decoder()
self.create_input_rail()
self.create_predecode_rail()
self.route_vdd_gnd()
def create_layout(self):
self.setup_layout_constants()
self.place_pre_decoder()
self.place_row_decoder()
self.route_input_rails()
self.route_predecode_rails()
self.route_vdd_gnd()
def add_modules(self): def add_modules(self):
self.inv = pinv() self.inv = pinv()
self.add_mod(self.inv) self.add_mod(self.inv)
@ -89,7 +95,7 @@ class hierarchical_decoder(design.design):
else: else:
debug.error("Invalid number of inputs for hierarchical decoder",-1) debug.error("Invalid number of inputs for hierarchical decoder",-1)
def setup_layout_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 rails to different groups. One group belongs to one pre-decoder.
@ -112,92 +118,9 @@ class hierarchical_decoder(design.design):
index = index + 1 index = index + 1
self.predec_groups.append(lines) self.predec_groups.append(lines)
self.calculate_dimensions()
def create_input_rail(self): def setup_layout_constants(self):
""" Create input rails for the predecoders """ """ Calculate the overall dimensions of the hierarchical decoder """
# 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
# Find the left-most predecoder
min_x = 0
if self.no_of_pre2x4 > 0:
min_x = min(min_x, -self.pre2_4.width)
if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width)
input_offset=vector(min_x - self.input_routing_width,0)
input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=input_height)
self.connect_input_to_predecodes()
def connect_input_to_predecodes(self):
""" Connect the vertical input rail to the predecoders """
for pre_num in range(self.no_of_pre2x4):
for i in range(2):
index = pre_num * 2 + i
input_pos = self.input_rails["addr[{}]".format(index)]
in_name = "in[{}]".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
self.connect_input_rail(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8):
for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pos = self.input_rails["addr[{}]".format(index)]
in_name = "in[{}]".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
self.connect_input_rail(decoder_offset, input_offset)
def connect_input_rail(self, input_offset, output_offset):
""" Connect a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=input_offset,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=output_offset,
rotate=90)
self.add_path(("metal3"), [input_offset, output_offset])
def add_pins(self):
""" Add the module pins """
for i in range(self.num_inputs):
self.add_pin("addr[{0}]".format(i))
for j in range(self.rows):
self.add_pin("decode[{0}]".format(j))
self.add_pin("vdd")
self.add_pin("gnd")
def calculate_dimensions(self):
""" Calculate the overal dimensions of the hierarchical decoder """
# If we have 4 or fewer rows, the predecoder is the decoder itself # If we have 4 or fewer rows, the predecoder is the decoder itself
if self.num_inputs>=4: if self.num_inputs>=4:
@ -227,24 +150,105 @@ class hierarchical_decoder(design.design):
self.height = self.row_decoder_height self.height = self.row_decoder_height
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 + nand_width + self.inv.width
def route_input_rails(self):
""" Create input rails for the predecoders """
# 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
# Find the left-most predecoder
min_x = 0
if self.no_of_pre2x4 > 0:
min_x = min(min_x, -self.pre2_4.width)
if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width)
input_offset=vector(min_x - self.input_routing_width,0)
input_bus_names = ["addr[{0}]".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=input_height)
self.route_input_to_predecodes()
def route_input_to_predecodes(self):
""" Route the vertical input rail to the predecoders """
for pre_num in range(self.no_of_pre2x4):
for i in range(2):
index = pre_num * 2 + i
input_pos = self.input_rails["addr[{}]".format(index)]
in_name = "in[{}]".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
self.route_input_rail(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8):
for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pos = self.input_rails["addr[{}]".format(index)]
in_name = "in[{}]".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
# To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0,(i+1)*self.inv.height)
input_offset = input_pos.scale(1,0) + decoder_offset.scale(0,1)
self.route_input_rail(decoder_offset, input_offset)
def route_input_rail(self, input_offset, output_offset):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=input_offset,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=output_offset,
rotate=90)
self.add_path(("metal3"), [input_offset, output_offset])
def add_pins(self):
""" Add the module pins """
for i in range(self.num_inputs):
self.add_pin("addr[{0}]".format(i))
for j in range(self.rows):
self.add_pin("decode[{0}]".format(j))
self.add_pin("vdd")
self.add_pin("gnd")
def create_pre_decoder(self): def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4): for i in range(self.no_of_pre2x4):
self.add_pre2x4(i) self.create_pre2x4(i)
for i in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
self.add_pre3x8(i) self.create_pre3x8(i)
def add_pre2x4(self,num): def create_pre2x4(self,num):
""" Add a 2x4 predecoder to the left of the origin """ """ Add a 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): if (self.num_inputs == 2):
base = vector(-self.pre2_4.width,0)
index_off1 = index_off2 = 0 index_off1 = index_off2 = 0
else: else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
index_off1 = num * 2 index_off1 = num * 2
index_off2 = num * 4 index_off2 = num * 4
@ -256,20 +260,12 @@ class hierarchical_decoder(design.design):
pins.extend(["vdd", "gnd"]) pins.extend(["vdd", "gnd"])
self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num), self.pre2x4_inst.append(self.add_inst(name="pre[{0}]".format(num),
mod=self.pre2_4, mod=self.pre2_4))
offset=base))
self.connect_inst(pins) self.connect_inst(pins)
def add_pre3x8(self,num): def create_pre3x8(self,num):
""" Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """ """ Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width,0)
mirror ="R0"
else:
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
offset = vector(-self.pre3_8.width, height)
# If we had 2x4 predecodes, those are used as the lower # If we had 2x4 predecodes, those are used as the lower
# decode output bits # decode output bits
in_index_offset = num * 3 + self.no_of_pre2x4 * 2 in_index_offset = num * 3 + self.no_of_pre2x4 * 2
@ -283,79 +279,112 @@ class hierarchical_decoder(design.design):
pins.extend(["vdd", "gnd"]) pins.extend(["vdd", "gnd"])
self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num), self.pre3x8_inst.append(self.add_inst(name="pre3x8[{0}]".format(num),
mod=self.pre3_8, mod=self.pre3_8))
offset=offset))
self.connect_inst(pins) self.connect_inst(pins)
def place_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """
for i in range(self.no_of_pre2x4):
self.place_pre2x4(i)
for i in range(self.no_of_pre3x8):
self.place_pre3x8(i)
def place_pre2x4(self,num):
""" Place 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2):
base = vector(-self.pre2_4.width,0)
else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
self.place_inst(name="pre[{0}]".format(num),
offset=base)
def place_pre3x8(self,num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width,0)
mirror ="R0"
else:
height = self.no_of_pre2x4*self.pre2_4.height + num*self.pre3_8.height
offset = vector(-self.pre3_8.width, height)
self.place_inst(name="pre3x8[{0}]".format(num),
offset=offset)
def create_row_decoder(self): def create_row_decoder(self):
""" Create the row-decoder by placing NAND2/NAND3 and Inverters """ Create the row-decoder by placing NAND2/NAND3 and Inverters
and add the primary decoder output pins. """ and add the primary decoder output pins. """
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
self.add_decoder_nand_array() self.create_decoder_nand_array()
self.add_decoder_inv_array() self.create_decoder_inv_array()
self.route_decoder()
def add_decoder_nand_array(self): def create_decoder_nand_array(self):
""" Add a column of NAND gates for final decode """ """ Add a column of NAND gates for final decode """
self.nand_inst = []
# Row Decoder NAND GATE array for address inputs <5. # Row Decoder NAND 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):
self.add_nand_array(nand_mod=self.nand2)
# FIXME: Can we convert this to the connect_inst with checks?
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[1])*i + j
name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand2))
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])),
"Z[{0}]".format(len(self.predec_groups[1])*i + j), "Z[{0}]".format(row),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(args=pins, check=False) self.connect_inst(pins)
# Row Decoder NAND GATE array for address inputs >5. # Row Decoder NAND GATE array for address inputs >5.
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
self.add_nand_array(nand_mod=self.nand3,
correct=drc["minwidth_metal1"])
# This will not check that the inst connections match.
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])):
Z_index = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \ row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
+ len(self.predec_groups[2])*j + k + len(self.predec_groups[2])*j + k
name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand3))
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])),
"Z[{0}]".format(Z_index), "Z[{0}]".format(row),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(args=pins, check=False) self.connect_inst(pins)
def add_nand_array(self, nand_mod, correct=0):
""" Add a column of NAND gates for the decoder above the predecoders.""" def create_decoder_inv_array(self):
"""
Add a column of INV gates for the decoder.
"""
self.nand_inst = [] self.inv_inst = []
for row in range(self.rows): for row in range(self.rows):
name = "DEC_NAND[{0}]".format(row) name = self.INV_FORMAT.format(row)
if ((row % 2) == 0): self.inv_inst.append(self.add_inst(name=name,
y_off = nand_mod.height*row mod=self.inv))
y_dir = 1 self.connect_inst(args=["Z[{0}]".format(row),
mirror = "R0" "decode[{0}]".format(row),
else: "vdd", "gnd"])
y_off = nand_mod.height*(row + 1)
y_dir = -1
mirror = "MX"
self.nand_inst.append(self.add_inst(name=name,
mod=nand_mod,
offset=[self.internal_routing_width, y_off],
mirror=mirror))
def place_decoder_inv_array(self):
"""
def add_decoder_inv_array(self): Place the column of INV gates for the decoder above the predecoders
"""Add a column of INV gates for the decoder above the predecoders and to the right of the NAND decoders.
and to the right of the NAND decoders.""" """
z_pin = self.inv.get_pin("Z") z_pin = self.inv.get_pin("Z")
@ -364,9 +393,8 @@ class hierarchical_decoder(design.design):
else: else:
x_off = self.internal_routing_width + self.nand3.width x_off = self.internal_routing_width + self.nand3.width
self.inv_inst = []
for row in range(self.rows): for row in range(self.rows):
name = "DEC_INV_[{0}]".format(row) name = self.INV_FORMAT.format(row)
if (row % 2 == 0): if (row % 2 == 0):
inv_row_height = self.inv.height * row inv_row_height = self.inv.height * row
mirror = "R0" mirror = "R0"
@ -377,17 +405,52 @@ class hierarchical_decoder(design.design):
y_dir = -1 y_dir = -1
y_off = inv_row_height y_off = inv_row_height
offset = vector(x_off,y_off) offset = vector(x_off,y_off)
self.place_inst(name=name,
self.inv_inst.append(self.add_inst(name=name, offset=offset,
mod=self.inv, mirror=mirror)
offset=offset,
mirror=mirror))
# This will not check that the inst connections match. def place_row_decoder(self):
self.connect_inst(args=["Z[{0}]".format(row), """
"decode[{0}]".format(row), Place the row-decoder by placing NAND2/NAND3 and Inverters
"vdd", "gnd"], and add the primary decoder output pins.
check=False) """
if (self.num_inputs >= 4):
self.place_decoder_nand_array()
self.place_decoder_inv_array()
self.route_decoder()
def place_decoder_nand_array(self):
""" Add a column of NAND gates for final decode """
# Row Decoder NAND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5):
self.place_nand_array(nand_mod=self.nand2)
# Row Decoder NAND GATE array for address inputs >5.
# FIXME: why this correct offset?)
elif (self.num_inputs > 5):
self.place_nand_array(nand_mod=self.nand3)
def place_nand_array(self, nand_mod):
""" Add a column of NAND gates for the decoder above the predecoders."""
for row in range(self.rows):
name = self.NAND_FORMAT.format(row)
if ((row % 2) == 0):
y_off = nand_mod.height*row
y_dir = 1
mirror = "R0"
else:
y_off = nand_mod.height*(row + 1)
y_dir = -1
mirror = "MX"
self.place_inst(name=name,
offset=[self.internal_routing_width, y_off],
mirror=mirror)
def route_decoder(self): def route_decoder(self):
@ -412,7 +475,7 @@ class hierarchical_decoder(design.design):
def create_predecode_rail(self): def route_predecode_rails(self):
""" Creates vertical metal 2 rails to connect predecoder and decoder stages.""" """ Creates vertical metal 2 rails 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.
@ -426,10 +489,10 @@ class hierarchical_decoder(design.design):
length=self.height) length=self.height)
self.connect_rails_to_predecodes() self.route_rails_to_predecodes()
self.connect_rails_to_decoder() self.route_rails_to_decoder()
def connect_rails_to_predecodes(self): def route_rails_to_predecodes(self):
""" Iterates through all of the predecodes and connects to the rails including the offsets """ """ Iterates through all of the predecodes and connects to the rails including the offsets """
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
@ -438,7 +501,7 @@ class hierarchical_decoder(design.design):
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.connect_predecode_rail_m3(predecode_name, pin) self.route_predecode_rail_m3(predecode_name, pin)
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
@ -447,11 +510,11 @@ 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.connect_predecode_rail_m3(predecode_name, pin) self.route_predecode_rail_m3(predecode_name, pin)
def connect_rails_to_decoder(self): def route_rails_to_decoder(self):
""" Use the self.predec_groups to determine the connections to the decoder NAND gates. """ Use the self.predec_groups to determine the connections to the decoder NAND gates.
Inputs of NAND2/NAND3 gates come from different groups. Inputs of NAND2/NAND3 gates come from different groups.
For example for these groups [ [0,1,2,3] ,[4,5,6,7], For example for these groups [ [0,1,2,3] ,[4,5,6,7],
@ -465,9 +528,9 @@ class hierarchical_decoder(design.design):
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
predecode_name = "predecode[{}]".format(index_A) predecode_name = "predecode[{}]".format(index_A)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
predecode_name = "predecode[{}]".format(index_B) predecode_name = "predecode[{}]".format(index_B)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
row_index = row_index + 1 row_index = row_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
@ -476,11 +539,11 @@ class hierarchical_decoder(design.design):
for index_C in self.predec_groups[2]: for index_C in self.predec_groups[2]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
predecode_name = "predecode[{}]".format(index_A) predecode_name = "predecode[{}]".format(index_A)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
predecode_name = "predecode[{}]".format(index_B) predecode_name = "predecode[{}]".format(index_B)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B"))
predecode_name = "predecode[{}]".format(index_C) predecode_name = "predecode[{}]".format(index_C)
self.connect_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C"))
row_index = row_index + 1 row_index = row_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -516,7 +579,7 @@ class hierarchical_decoder(design.design):
self.copy_layout_pin(pre, "gnd") self.copy_layout_pin(pre, "gnd")
def connect_predecode_rail(self, rail_name, pin): def route_predecode_rail(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """ """ Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y) rail_pos = vector(self.predecode_rails[rail_name].x,pin.lc().y)
self.add_path("metal1", [rail_pos, pin.lc()]) self.add_path("metal1", [rail_pos, pin.lc()])
@ -525,7 +588,7 @@ class hierarchical_decoder(design.design):
rotate=90) rotate=90)
def connect_predecode_rail_m3(self, rail_name, pin): def route_predecode_rail_m3(self, rail_name, pin):
""" Connect the routing rail to the given metal1 pin """ """ Connect the routing rail to the given metal1 pin """
# 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.

View File

@ -65,7 +65,7 @@ class hierarchical_predecode(design.design):
# Height width are computed # Height width are computed
self.width = self.x_off_inv_2 + self.inv.width self.width = self.x_off_inv_2 + self.inv.width
def create_rails(self): def route_rails(self):
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ """ Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)] input_names = ["in[{}]".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5*self.m2_width,2*self.m1_width) offset = vector(0.5*self.m2_width,2*self.m1_width)
@ -86,10 +86,21 @@ class hierarchical_predecode(design.design):
length=self.height - 2*self.m1_width) length=self.height - 2*self.m1_width)
def add_input_inverters(self): def create_input_inverters(self):
""" Create the input inverters to invert input signals for the decode stage. """ """ Create the input inverters to invert input signals for the decode stage. """
self.in_inst = [] self.in_inst = []
for inv_num in range(self.number_of_inputs):
name = "Xpre_inv[{0}]".format(inv_num)
self.in_inst.append(self.add_inst(name=name,
mod=self.inv))
self.connect_inst(["in[{0}]".format(inv_num),
"inbar[{0}]".format(inv_num),
"vdd", "gnd"])
def place_input_inverters(self):
""" Place the input inverters to invert input signals for the decode stage. """
for inv_num in range(self.number_of_inputs): for inv_num in range(self.number_of_inputs):
name = "Xpre_inv[{0}]".format(inv_num) name = "Xpre_inv[{0}]".format(inv_num)
if (inv_num % 2 == 0): if (inv_num % 2 == 0):
@ -99,18 +110,26 @@ class hierarchical_predecode(design.design):
y_off = (inv_num + 1) * (self.inv.height) y_off = (inv_num + 1) * (self.inv.height)
mirror="MX" mirror="MX"
offset = vector(self.x_off_inv_1, y_off) offset = vector(self.x_off_inv_1, y_off)
self.in_inst.append(self.add_inst(name=name, self.place_inst(name=name,
mod=self.inv, offset=offset,
offset=offset, mirror=mirror)
mirror=mirror))
self.connect_inst(["in[{0}]".format(inv_num),
"inbar[{0}]".format(inv_num),
"vdd", "gnd"])
def add_output_inverters(self): def create_output_inverters(self):
""" Create inverters for the inverted output decode signals. """ """ Create inverters for the inverted output decode signals. """
self.inv_inst = [] self.inv_inst = []
for inv_num in range(self.number_of_outputs):
name = "Xpre_nand_inv[{}]".format(inv_num)
self.inv_inst.append(self.add_inst(name=name,
mod=self.inv))
self.connect_inst(["Z[{}]".format(inv_num),
"out[{}]".format(inv_num),
"vdd", "gnd"])
def place_output_inverters(self):
""" Place inverters for the inverted output decode signals. """
for inv_num in range(self.number_of_outputs): for inv_num in range(self.number_of_outputs):
name = "Xpre_nand_inv[{}]".format(inv_num) name = "Xpre_nand_inv[{}]".format(inv_num)
if (inv_num % 2 == 0): if (inv_num % 2 == 0):
@ -120,19 +139,23 @@ class hierarchical_predecode(design.design):
y_off =(inv_num + 1)*self.inv.height y_off =(inv_num + 1)*self.inv.height
mirror = "MX" mirror = "MX"
offset = vector(self.x_off_inv_2, y_off) offset = vector(self.x_off_inv_2, y_off)
self.inv_inst.append(self.add_inst(name=name, self.place_inst(name=name,
mod=self.inv, offset=offset,
offset=offset, mirror=mirror)
mirror=mirror))
self.connect_inst(["Z[{}]".format(inv_num),
"out[{}]".format(inv_num),
"vdd", "gnd"])
def add_nand(self,connections): def create_nand_array(self,connections):
""" Create the NAND stage for the decodes """ """ Create the NAND stage for the decodes """
self.nand_inst = [] self.nand_inst = []
for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand))
self.connect_inst(connections[nand_input])
def place_nand_array(self):
""" Place the NAND stage for the decodes """
for nand_input in range(self.number_of_outputs): for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs) inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
name = "Xpre{0}_nand[{1}]".format(inout,nand_input) name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
@ -143,11 +166,9 @@ class hierarchical_predecode(design.design):
y_off = (nand_input + 1) * self.inv.height y_off = (nand_input + 1) * self.inv.height
mirror = "MX" mirror = "MX"
offset = vector(self.x_off_nand, y_off) offset = vector(self.x_off_nand, y_off)
self.nand_inst.append(self.add_inst(name=name, self.place_inst(name=name,
mod=self.nand, offset=offset,
offset=offset, mirror=mirror)
mirror=mirror))
self.connect_inst(connections[nand_input])
def route(self): def route(self):

View File

@ -14,8 +14,17 @@ class hierarchical_predecode2x4(hierarchical_predecode):
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
self.setup_constraints() self.setup_constraints()
self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def create_netlist(self):
self.create_input_inverters()
self.create_output_inverters()
connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"],
["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]]
self.create_nand_array(connections)
def create_layout(self): def create_layout(self):
""" The general organization is from left to right: """ The general organization is from left to right:
@ -24,15 +33,12 @@ class hierarchical_predecode2x4(hierarchical_predecode):
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs 3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
4) a set of NAND gates for inversion 4) a set of NAND gates for inversion
""" """
self.create_rails() self.route_rails()
self.add_input_inverters() self.place_input_inverters()
self.add_output_inverters() self.place_output_inverters()
connections =[["inbar[0]", "inbar[1]", "Z[0]", "vdd", "gnd"], self.place_nand_array()
["in[0]", "inbar[1]", "Z[1]", "vdd", "gnd"],
["inbar[0]", "in[1]", "Z[2]", "vdd", "gnd"],
["in[0]", "in[1]", "Z[3]", "vdd", "gnd"]]
self.add_nand(connections)
self.route() self.route()
self.DRC_LVS()
def get_nand_input_line_combination(self): def get_nand_input_line_combination(self):
""" These are the decoder connections of the NAND gates to the A,B pins """ """ These are the decoder connections of the NAND gates to the A,B pins """

View File

@ -14,19 +14,12 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.add_pins() self.add_pins()
self.create_modules() self.create_modules()
self.setup_constraints() self.setup_constraints()
self.create_netlist()
self.create_layout() self.create_layout()
self.DRC_LVS()
def create_layout(self): def create_netlist(self):
""" The general organization is from left to right: self.create_input_inverters()
1) a set of M2 rails for input signals self.create_output_inverters()
2) a set of inverters to invert input signals
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
4) a set of NAND gates for inversion
"""
self.create_rails()
self.add_input_inverters()
self.add_output_inverters()
connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"], connections=[["inbar[0]", "inbar[1]", "inbar[2]", "Z[0]", "vdd", "gnd"],
["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"], ["in[0]", "inbar[1]", "inbar[2]", "Z[1]", "vdd", "gnd"],
["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"], ["inbar[0]", "in[1]", "inbar[2]", "Z[2]", "vdd", "gnd"],
@ -35,9 +28,23 @@ class hierarchical_predecode3x8(hierarchical_predecode):
["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"], ["in[0]", "inbar[1]", "in[2]", "Z[5]", "vdd", "gnd"],
["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"], ["inbar[0]", "in[1]", "in[2]", "Z[6]", "vdd", "gnd"],
["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]] ["in[0]", "in[1]", "in[2]", "Z[7]", "vdd", "gnd"]]
self.add_nand(connections) self.create_nand(connections)
self.route()
def create_layout(self):
"""
The general organization is from left to right:
1) a set of M2 rails for input signals
2) a set of inverters to invert input signals
3) a set of M2 rails for the vdd, gnd, inverted inputs, inputs
4) a set of NAND gates for inversion
"""
self.route_rails()
self.place_input_inverters()
self.place_output_inverters()
self.place_nand()
self.route()
self.DRC_LVS()
def get_nand_input_line_combination(self): def get_nand_input_line_combination(self):
""" These are the decoder connections of the NAND gates to the A,B,C pins """ """ These are the decoder connections of the NAND gates to the A,B,C pins """
combination = [["Abar[0]", "Abar[1]", "Abar[2]"], combination = [["Abar[0]", "Abar[1]", "Abar[2]"],