Convert pnand+pinv to pand in decoders.

This commit is contained in:
mrg 2020-03-06 13:26:40 -08:00
parent 1a2efd77ad
commit 23501c7b35
5 changed files with 143 additions and 249 deletions

View File

@ -20,8 +20,7 @@ class hierarchical_decoder(design.design):
def __init__(self, name, rows): def __init__(self, name, rows):
design.design.__init__(self, name) design.design.__init__(self, name)
self.NAND_FORMAT = "DEC_NAND_{0}" self.AND_FORMAT = "DEC_AND_{0}"
self.INV_FORMAT = "DEC_INV_{0}"
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
@ -58,12 +57,12 @@ class hierarchical_decoder(design.design):
self.inv = factory.create(module_type="pinv", self.inv = factory.create(module_type="pinv",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.inv) self.add_mod(self.inv)
self.nand2 = factory.create(module_type="pnand2", self.and2 = factory.create(module_type="pand2",
height=self.cell_height)
self.add_mod(self.and2)
self.and3 = factory.create(module_type="pand3",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.nand2) self.add_mod(self.and3)
self.nand3 = factory.create(module_type="pnand3",
height=self.cell_height)
self.add_mod(self.nand3)
self.add_decoders() self.add_decoders()
@ -143,9 +142,9 @@ class hierarchical_decoder(design.design):
# 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.nand2.width nand_width = self.and2.width
else: else:
nand_width = self.nand3.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
self.row_decoder_height = self.inv.height * self.rows self.row_decoder_height = self.inv.height * self.rows
@ -309,33 +308,32 @@ class hierarchical_decoder(design.design):
self.pre3x8_inst[num].place(offset) self.pre3x8_inst[num].place(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 AND2/AND3 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.create_decoder_nand_array() self.create_decoder_and_array()
self.create_decoder_inv_array()
def create_decoder_nand_array(self): def create_decoder_and_array(self):
""" Add a column of NAND gates for final decode """ """ Add a column of AND gates for final decode """
self.nand_inst = [] self.and_inst = []
# Row Decoder NAND 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):
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 row = len(self.predec_groups[0]) * j + i
if (row < self.rows): if (row < self.rows):
name = self.NAND_FORMAT.format(row) name = self.AND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.nand2)) 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])),
"Z_{0}".format(row), "decode_{0}".format(row),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) self.connect_inst(pins)
# Row Decoder NAND GATE array for address inputs >5. # Row Decoder AND GATE array for address inputs >5.
elif (self.num_inputs > 5): elif (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])):
@ -344,104 +342,57 @@ class hierarchical_decoder(design.design):
+ len(self.predec_groups[0]) * j + i + len(self.predec_groups[0]) * j + i
if (row < self.rows): if (row < self.rows):
name = self.NAND_FORMAT.format(row) name = self.AND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.nand3)) 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])),
"Z_{0}".format(row), "decode_{0}".format(row),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) self.connect_inst(pins)
def create_decoder_inv_array(self):
"""
Add a column of INV gates for the decoder.
"""
self.inv_inst = []
for row in range(self.rows):
name = self.INV_FORMAT.format(row)
self.inv_inst.append(self.add_inst(name=name,
mod=self.inv))
self.connect_inst(args=["Z_{0}".format(row),
"decode_{0}".format(row),
"vdd", "gnd"])
def place_decoder_inv_array(self):
"""
Place the column of INV gates for the decoder above the predecoders
and to the right of the NAND decoders.
"""
if (self.num_inputs == 4 or self.num_inputs == 5):
x_off = self.internal_routing_width + self.nand2.width
else:
x_off = self.internal_routing_width + self.nand3.width
for row in range(self.rows):
if (row % 2 == 0):
inv_row_height = self.inv.height * row
mirror = "R0"
else:
inv_row_height = self.inv.height * (row + 1)
mirror = "MX"
y_off = inv_row_height
offset = vector(x_off, y_off)
self.inv_inst[row].place(offset=offset,
mirror=mirror)
def place_row_decoder(self): def place_row_decoder(self):
""" """
Place the row-decoder by placing NAND2/NAND3 and Inverters Place the row-decoder by placing AND2/AND3 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.place_decoder_nand_array() self.place_decoder_and_array()
self.place_decoder_inv_array()
self.route_decoder() self.route_decoder()
def place_decoder_nand_array(self): def place_decoder_and_array(self):
""" Add a column of NAND gates for final decode """ """ Add a column of AND gates for final decode """
# Row Decoder NAND 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):
self.place_nand_array(nand_mod=self.nand2) self.place_and_array(and_mod=self.and2)
# Row Decoder NAND GATE array for address inputs >5. # Row Decoder AND GATE array for address inputs >5.
# FIXME: why this correct offset?) # FIXME: why this correct offset?)
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
self.place_nand_array(nand_mod=self.nand3) self.place_and_array(and_mod=self.and3)
def place_nand_array(self, nand_mod): def place_and_array(self, and_mod):
""" Add a column of NAND 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 row in range(self.rows):
if ((row % 2) == 0): if ((row % 2) == 0):
y_off = nand_mod.height * row y_off = and_mod.height * row
mirror = "R0" mirror = "R0"
else: else:
y_off = nand_mod.height * (row + 1) y_off = and_mod.height * (row + 1)
mirror = "MX" mirror = "MX"
self.nand_inst[row].place(offset=[self.internal_routing_width, y_off], self.and_inst[row].place(offset=[self.internal_routing_width, y_off],
mirror=mirror) mirror=mirror)
def route_decoder(self): def route_decoder(self):
""" Route the nand to inverter in the decoder and add the pins. """ """ Add the pins. """
for row in range(self.rows): for row in range(self.rows):
z_pin = self.and_inst[row].get_pin("Z")
# route nand output to output inv input
zr_pos = self.nand_inst[row].get_pin("Z").rc()
al_pos = self.inv_inst[row].get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5 * (zr_pos.x + al_pos.x), zr_pos.y)
mid2_pos = vector(0.5 * (zr_pos.x + al_pos.x), al_pos.y)
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pin = self.inv_inst[row].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(row), self.add_layout_pin(text="decode_{0}".format(row),
layer="m1", layer="m1",
offset=z_pin.ll(), offset=z_pin.ll(),
@ -484,12 +435,12 @@ class hierarchical_decoder(design.design):
self.route_predecode_rail_m3(predecode_name, pin) self.route_predecode_rail_m3(predecode_name, pin)
def route_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 AND gates.
Inputs of NAND2/NAND3 gates come from different groups. Inputs of AND2/AND3 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],
[8,9,10,11,12,13,14,15] ] the first NAND3 inputs are connected to [8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
[0,4,8] and second NAND3 is connected to [0,4,9] ........... and the [0,4,8] and second AND3 is connected to [0,4,9] ........... and the
128th NAND3 is connected to [3,7,15] 128th AND3 is connected to [3,7,15]
""" """
row_index = 0 row_index = 0
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
@ -498,9 +449,9 @@ class hierarchical_decoder(design.design):
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (row_index < self.rows): if (row_index < self.rows):
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) self.route_predecode_rail(predecode_name, self.and_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):
@ -510,35 +461,25 @@ class hierarchical_decoder(design.design):
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (row_index < self.rows): if (row_index < self.rows):
predecode_name = "predecode_{}".format(index_A) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A")) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("B")) self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
predecode_name = "predecode_{}".format(index_C) predecode_name = "predecode_{}".format(index_C)
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("C")) self.route_predecode_rail(predecode_name, self.and_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):
""" 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 in the center and right of the cells, respectively.
xoffset = self.nand_inst[0].rx() xoffset = self.and_inst[0].rx()
for num in range(0, self.rows): for num in range(0, self.rows):
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.nand_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_power_pin(name=pin_name, self.add_power_pin(name=pin_name,
loc=pin_pos) loc=pin_pos)
# Make a redundant rail too
for num in range(0, self.rows, 2):
for pin_name in ["vdd", "gnd"]:
start = self.nand_inst[num].get_pin(pin_name).lc()
end = self.inv_inst[num].get_pin(pin_name).rc()
mid = (start + end).scale(0.5, 0.5)
self.add_rect_center(layer="m1",
offset=mid,
width=end.x - start.x)
# Copy the pins from the predecoders # Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst: for pre in self.pre2x4_inst + self.pre3x8_inst:

View File

@ -32,61 +32,58 @@ class hierarchical_predecode(design.design):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
""" Add the INV and NAND gate modules """ """ Add the INV and AND gate modules """
self.inv = factory.create(module_type="pinv", self.inv = factory.create(module_type="pinv",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.inv) self.add_mod(self.inv)
self.add_nand(self.number_of_inputs) self.add_and(self.number_of_inputs)
self.add_mod(self.nand) self.add_mod(self.and_mod)
def add_nand(self, inputs): def add_and(self, inputs):
""" Create the NAND for the predecode input stage """ """ Create the NAND for the predecode input stage """
if inputs==2: if inputs==2:
self.nand = factory.create(module_type="pnand2", self.and_mod = factory.create(module_type="pand2",
height=self.cell_height) height=self.cell_height)
elif inputs==3: elif inputs==3:
self.nand = factory.create(module_type="pnand3", self.and_mod = factory.create(module_type="pand3",
height=self.cell_height) height=self.cell_height)
else: else:
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1) debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
def setup_layout_constraints(self): def setup_layout_constraints(self):
self.height = self.number_of_outputs * self.nand.height self.height = self.number_of_outputs * self.and_mod.height
# x offset for input inverters # x offset for input inverters
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
# x offset to NAND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches # x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
# x offset to output inverters # x offset to output inverters
self.x_off_inv_2 = self.x_off_nand + self.nand.width self.width = self.x_off_and + self.and_mod.width
# Height width are computed
self.width = self.x_off_inv_2 + self.inv.width
def route_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, self.m1_pitch)
self.input_rails = self.create_vertical_pin_bus(layer="m2", self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=offset, offset=offset,
names=input_names, names=input_names,
length=self.height - 2*self.m1_width) length=self.height - 2 * self.m1_pitch)
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)] invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)] non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
decode_names = invert_names + non_invert_names decode_names = invert_names + non_invert_names
offset = vector(self.x_off_inv_1 + self.inv.width + 2*self.m2_pitch, 2*self.m1_width) offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m1_pitch)
self.decode_rails = self.create_vertical_bus(layer="m2", self.decode_rails = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=offset, offset=offset,
names=decode_names, names=decode_names,
length=self.height - 2*self.m1_width) length=self.height - 2 * self.m1_pitch)
def create_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. """
@ -112,62 +109,35 @@ class hierarchical_predecode(design.design):
self.in_inst[inv_num].place(offset=offset, self.in_inst[inv_num].place(offset=offset,
mirror=mirror) mirror=mirror)
def create_output_inverters(self): def create_and_array(self, connections):
""" Create inverters for the inverted output decode signals. """ """ Create the AND stage for the decodes """
self.inv_inst = [] self.and_inst = []
for inv_num in range(self.number_of_outputs): for and_input in range(self.number_of_outputs):
name = "pre_nand_inv_{}".format(inv_num) inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
self.inv_inst.append(self.add_inst(name=name, name = "Xpre{0}_and_{1}".format(inout, and_input)
mod=self.inv)) self.and_inst.append(self.add_inst(name=name,
self.connect_inst(["Z_{}".format(inv_num), mod=self.and_mod))
"out_{}".format(inv_num), self.connect_inst(connections[and_input])
"vdd", "gnd"])
def place_and_array(self):
def place_output_inverters(self): """ Place the AND stage for the decodes """
""" Place inverters for the inverted output decode signals. """ for and_input in range(self.number_of_outputs):
for inv_num in range(self.number_of_outputs): # inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
if (inv_num % 2 == 0): if (and_input % 2 == 0):
y_off = inv_num * self.inv.height y_off = and_input * self.and_mod.height
mirror = "R0" mirror = "R0"
else: else:
y_off =(inv_num + 1)*self.inv.height y_off = (and_input + 1) * self.and_mod.height
mirror = "MX" mirror = "MX"
offset = vector(self.x_off_inv_2, y_off) offset = vector(self.x_off_and, y_off)
self.inv_inst[inv_num].place(offset=offset, self.and_inst[and_input].place(offset=offset,
mirror=mirror) mirror=mirror)
def create_nand_array(self,connections):
""" Create the NAND stage for the decodes """
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):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
if (nand_input % 2 == 0):
y_off = nand_input * self.inv.height
mirror = "R0"
else:
y_off = (nand_input + 1) * self.inv.height
mirror = "MX"
offset = vector(self.x_off_nand, y_off)
self.nand_inst[nand_input].place(offset=offset,
mirror=mirror)
def route(self): def route(self):
self.route_input_inverters() self.route_input_inverters()
self.route_inputs_to_rails() self.route_inputs_to_rails()
self.route_nand_to_rails() self.route_and_to_rails()
self.route_output_inverters() self.route_output_and()
self.route_vdd_gnd() self.route_vdd_gnd()
def route_inputs_to_rails(self): def route_inputs_to_rails(self):
@ -175,39 +145,30 @@ class hierarchical_predecode(design.design):
for num in range(self.number_of_inputs): for num in range(self.number_of_inputs):
# route one signal next to each vdd/gnd rail since this is # route one signal next to each vdd/gnd rail since this is
# typically where the p/n devices are and there are no # typically where the p/n devices are and there are no
# pins in the nand gates. # pins in the and gates.
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
in_pin = "in_{}".format(num) in_pin = "in_{}".format(num)
a_pin = "A_{}".format(num) a_pin = "A_{}".format(num)
in_pos = vector(self.input_rails[in_pin].x,y_offset) in_pos = vector(self.input_rails[in_pin].x, y_offset)
a_pos = vector(self.decode_rails[a_pin].x,y_offset) a_pos = vector(self.decode_rails[a_pin].x, y_offset)
self.add_path("m1",[in_pos, a_pos]) self.add_path("m1", [in_pos, a_pos])
self.add_via_center(layers = self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=[self.input_rails[in_pin].x, y_offset]) offset=[self.input_rails[in_pin].x, y_offset])
self.add_via_center(layers = self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=[self.decode_rails[a_pin].x, y_offset]) offset=[self.decode_rails[a_pin].x, y_offset])
def route_output_inverters(self): def route_output_and(self):
""" """
Route all conections of the outputs inverters Route all conections of the outputs and gates
""" """
for num in range(self.number_of_outputs): for num in range(self.number_of_outputs):
# route nand output to output inv input z_pin = self.and_inst[num].get_pin("Z")
zr_pos = self.nand_inst[num].get_pin("Z").rc()
al_pos = self.inv_inst[num].get_pin("A").lc()
# ensure the bend is in the middle
mid1_pos = vector(0.5*(zr_pos.x+al_pos.x), zr_pos.y)
mid2_pos = vector(0.5*(zr_pos.x+al_pos.x), al_pos.y)
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
z_pin = self.inv_inst[num].get_pin("Z")
self.add_layout_pin(text="out_{}".format(num), self.add_layout_pin(text="out_{}".format(num),
layer="m1", layer="m1",
offset=z_pin.ll(), offset=z_pin.ll(),
height=z_pin.height(), height=z_pin.height(),
width=z_pin.width()) width=z_pin.width())
def route_input_inverters(self): def route_input_inverters(self):
""" """
@ -219,7 +180,7 @@ class hierarchical_predecode(design.design):
#add output so that it is just below the vdd or gnd rail #add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no # since this is where the p/n devices are and there are no
# pins in the nand gates. # pins in the and gates.
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc() inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc()
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0) right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0)
@ -236,13 +197,12 @@ class hierarchical_predecode(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=in_pos) offset=in_pos)
def route_and_to_rails(self):
def route_nand_to_rails(self): # This 2D array defines the connection mapping
# This 2D array defines the connection mapping and_input_line_combination = self.get_and_input_line_combination()
nand_input_line_combination = self.get_nand_input_line_combination()
for k in range(self.number_of_outputs): for k in range(self.number_of_outputs):
# create x offset list # create x offset list
index_lst= nand_input_line_combination[k] index_lst= and_input_line_combination[k]
if self.number_of_inputs == 2: if self.number_of_inputs == 2:
gate_lst = ["A","B"] gate_lst = ["A","B"]
@ -251,35 +211,32 @@ class hierarchical_predecode(design.design):
# this will connect pins A,B or A,B,C # this will connect pins A,B or A,B,C
for rail_pin,gate_pin in zip(index_lst,gate_lst): for rail_pin,gate_pin in zip(index_lst,gate_lst):
pin_pos = self.nand_inst[k].get_pin(gate_pin).lc() pin_pos = self.and_inst[k].get_pin(gate_pin).lc()
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("m1", [rail_pos, pin_pos]) self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=rail_pos) offset=rail_pos)
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. """
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
in_xoffset = self.in_inst[0].rx() + self.m1_space in_xoffset = self.in_inst[0].rx() + self.m1_space
out_xoffset = self.inv_inst[0].lx() - self.m1_space # out_xoffset = self.and_inst[0].cx() + self.m1_space
for num in range(0,self.number_of_outputs): for num in range(0, self.number_of_outputs):
# this will result in duplicate polygons for rails, but who cares # this will result in duplicate polygons for rails, but who cares
# Route both supplies # Route both supplies
for n in ["vdd", "gnd"]: for n in ["vdd", "gnd"]:
nand_pin = self.nand_inst[num].get_pin(n) and_pin = self.and_inst[num].get_pin(n)
supply_offset = nand_pin.ll().scale(0,1) supply_offset = and_pin.ll().scale(0, 1)
self.add_rect(layer="m1", self.add_rect(layer="m1",
offset=supply_offset, offset=supply_offset,
width=self.inv_inst[num].rx()) width=self.and_inst[num].rx())
# Add pins in two locations # Add pins in two locations
for xoffset in [in_xoffset, out_xoffset]: for xoffset in [in_xoffset]:
pin_pos = vector(xoffset, nand_pin.cy()) pin_pos = vector(xoffset, and_pin.cy())
self.add_power_pin(n, pin_pos) self.add_power_pin(n, pin_pos)

View File

@ -27,33 +27,31 @@ class hierarchical_predecode2x4(hierarchical_predecode):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_input_inverters() self.create_input_inverters()
self.create_output_inverters() connections =[["inbar_0", "inbar_1", "out_0", "vdd", "gnd"],
connections =[["inbar_0", "inbar_1", "Z_0", "vdd", "gnd"], ["in_0", "inbar_1", "out_1", "vdd", "gnd"],
["in_0", "inbar_1", "Z_1", "vdd", "gnd"], ["inbar_0", "in_1", "out_2", "vdd", "gnd"],
["inbar_0", "in_1", "Z_2", "vdd", "gnd"], ["in_0", "in_1", "out_3", "vdd", "gnd"]]
["in_0", "in_1", "Z_3", "vdd", "gnd"]] self.create_and_array(connections)
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:
1) a set of M2 rails for input signals 1) a set of M2 rails for input signals
2) a set of inverters to invert input signals 2) a set of inverters to invert input signals
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 AND gates for inversion
""" """
self.setup_layout_constraints() self.setup_layout_constraints()
self.route_rails() self.route_rails()
self.place_input_inverters() self.place_input_inverters()
self.place_output_inverters() self.place_and_array()
self.place_nand_array()
self.route() self.route()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def get_nand_input_line_combination(self): def get_and_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 AND gates to the A,B pins """
combination = [["Abar_0", "Abar_1"], combination = [["Abar_0", "Abar_1"],
["A_0", "Abar_1"], ["A_0", "Abar_1"],
["Abar_0", "A_1"], ["Abar_0", "A_1"],
["A_0", "A_1"]] ["A_0", "A_1"]]
return combination return combination

View File

@ -20,26 +20,25 @@ class hierarchical_predecode3x8(hierarchical_predecode):
hierarchical_predecode.__init__(self, name, 3, height) hierarchical_predecode.__init__(self, name, 3, height)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
self.create_input_inverters() self.create_input_inverters()
self.create_output_inverters() connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"], ["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"], ["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"], ["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"], ["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"], ["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"], ["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"], ["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]] self.create_and_array(connections)
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:
1) a set of M2 rails for input signals 1) a set of M2 rails for input signals
2) a set of inverters to invert input signals 2) a set of inverters to invert input signals
@ -49,20 +48,19 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.setup_layout_constraints() self.setup_layout_constraints()
self.route_rails() self.route_rails()
self.place_input_inverters() self.place_input_inverters()
self.place_output_inverters() self.place_and_array()
self.place_nand_array()
self.route() self.route()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def get_nand_input_line_combination(self): def get_and_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"],
["A_0", "Abar_1", "Abar_2"], ["A_0", "Abar_1", "Abar_2"],
["Abar_0", "A_1", "Abar_2"], ["Abar_0", "A_1", "Abar_2"],
["A_0", "A_1", "Abar_2"], ["A_0", "A_1", "Abar_2"],
["Abar_0", "Abar_1", "A_2"], ["Abar_0", "Abar_1", "A_2"],
["A_0", "Abar_1", "A_2"], ["A_0", "Abar_1", "A_2"],
["Abar_0", "A_1", "A_2"], ["Abar_0", "A_1", "A_2"],
["A_0", "A_1", "A_2"]] ["A_0", "A_1", "A_2"]]
return combination return combination

View File

@ -185,6 +185,7 @@ class pnand3(pgate.pgate):
def route_inputs(self): def route_inputs(self):
""" Route the A and B and C inputs """ """ Route the A and B and C inputs """
m1_pitch = self.m1_space + contact.m1_via.first_layer_height
# Put B right on the well line # Put B right on the well line
self.inputB_yoffset = self.nwell_y_offset self.inputB_yoffset = self.nwell_y_offset
self.route_input_gate(self.pmos2_inst, self.route_input_gate(self.pmos2_inst,
@ -193,20 +194,19 @@ class pnand3(pgate.pgate):
"B", "B",
position="center") position="center")
self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch self.inputC_yoffset = self.inputB_yoffset - m1_pitch
self.route_input_gate(self.pmos3_inst, self.route_input_gate(self.pmos3_inst,
self.nmos3_inst, self.nmos3_inst,
self.inputC_yoffset, self.inputC_yoffset,
"C", "C",
position="center") position="center")
self.inputA_yoffset = self.inputB_yoffset + self.m1_pitch self.inputA_yoffset = self.inputB_yoffset + 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="center")
def route_output(self): def route_output(self):
""" Route the Z output """ """ Route the Z output """