mirror of https://github.com/VLSIDA/OpenRAM.git
Convert pnand+pinv to pand in decoders.
This commit is contained in:
parent
1a2efd77ad
commit
23501c7b35
|
|
@ -20,8 +20,7 @@ class hierarchical_decoder(design.design):
|
|||
def __init__(self, name, rows):
|
||||
design.design.__init__(self, name)
|
||||
|
||||
self.NAND_FORMAT = "DEC_NAND_{0}"
|
||||
self.INV_FORMAT = "DEC_INV_{0}"
|
||||
self.AND_FORMAT = "DEC_AND_{0}"
|
||||
|
||||
self.pre2x4_inst = []
|
||||
self.pre3x8_inst = []
|
||||
|
|
@ -58,12 +57,12 @@ class hierarchical_decoder(design.design):
|
|||
self.inv = factory.create(module_type="pinv",
|
||||
height=self.cell_height)
|
||||
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.nand2)
|
||||
self.nand3 = factory.create(module_type="pnand3",
|
||||
self.add_mod(self.and2)
|
||||
self.and3 = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.nand3)
|
||||
self.add_mod(self.and3)
|
||||
|
||||
self.add_decoders()
|
||||
|
||||
|
|
@ -143,9 +142,9 @@ class hierarchical_decoder(design.design):
|
|||
|
||||
# Calculates height and width of row-decoder
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
nand_width = self.nand2.width
|
||||
nand_width = self.and2.width
|
||||
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.row_decoder_height = self.inv.height * self.rows
|
||||
|
||||
|
|
@ -309,33 +308,32 @@ class hierarchical_decoder(design.design):
|
|||
self.pre3x8_inst[num].place(offset)
|
||||
|
||||
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. """
|
||||
if (self.num_inputs >= 4):
|
||||
self.create_decoder_nand_array()
|
||||
self.create_decoder_inv_array()
|
||||
self.create_decoder_and_array()
|
||||
|
||||
def create_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
def create_decoder_and_array(self):
|
||||
""" 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):
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
for j in range(len(self.predec_groups[1])):
|
||||
row = len(self.predec_groups[0]) * j + i
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand2))
|
||||
name = self.AND_FORMAT.format(row)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and2))
|
||||
pins =["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
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):
|
||||
for i in range(len(self.predec_groups[0])):
|
||||
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
|
||||
|
||||
if (row < self.rows):
|
||||
name = self.NAND_FORMAT.format(row)
|
||||
self.nand_inst.append(self.add_inst(name=name,
|
||||
mod=self.nand3))
|
||||
name = self.AND_FORMAT.format(row)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and3))
|
||||
|
||||
pins = ["out_{0}".format(i),
|
||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||
"Z_{0}".format(row),
|
||||
"decode_{0}".format(row),
|
||||
"vdd", "gnd"]
|
||||
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):
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
if (self.num_inputs >= 4):
|
||||
self.place_decoder_nand_array()
|
||||
self.place_decoder_inv_array()
|
||||
self.place_decoder_and_array()
|
||||
self.route_decoder()
|
||||
|
||||
def place_decoder_nand_array(self):
|
||||
""" Add a column of NAND gates for final decode """
|
||||
def place_decoder_and_array(self):
|
||||
""" 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):
|
||||
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?)
|
||||
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):
|
||||
""" Add a column of NAND gates for the decoder above the predecoders."""
|
||||
def place_and_array(self, and_mod):
|
||||
""" Add a column of AND gates for the decoder above the predecoders."""
|
||||
|
||||
for row in range(self.rows):
|
||||
if ((row % 2) == 0):
|
||||
y_off = nand_mod.height * row
|
||||
y_off = and_mod.height * row
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off = nand_mod.height * (row + 1)
|
||||
y_off = and_mod.height * (row + 1)
|
||||
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)
|
||||
|
||||
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):
|
||||
|
||||
# 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")
|
||||
z_pin = self.and_inst[row].get_pin("Z")
|
||||
self.add_layout_pin(text="decode_{0}".format(row),
|
||||
layer="m1",
|
||||
offset=z_pin.ll(),
|
||||
|
|
@ -484,12 +435,12 @@ class hierarchical_decoder(design.design):
|
|||
self.route_predecode_rail_m3(predecode_name, pin)
|
||||
|
||||
def route_rails_to_decoder(self):
|
||||
""" Use the self.predec_groups to determine the connections to the decoder NAND gates.
|
||||
Inputs of NAND2/NAND3 gates come from different groups.
|
||||
""" 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 NAND3 inputs are connected to
|
||||
[0,4,8] and second NAND3 is connected to [0,4,9] ........... and the
|
||||
128th NAND3 is connected to [3,7,15]
|
||||
[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
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
|
|
@ -498,9 +449,9 @@ class hierarchical_decoder(design.design):
|
|||
# FIXME: convert to connect_bus?
|
||||
if (row_index < self.rows):
|
||||
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)
|
||||
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
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
|
|
@ -510,36 +461,26 @@ class hierarchical_decoder(design.design):
|
|||
# FIXME: convert to connect_bus?
|
||||
if (row_index < self.rows):
|
||||
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)
|
||||
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)
|
||||
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
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" 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.
|
||||
xoffset = self.nand_inst[0].rx()
|
||||
xoffset = self.and_inst[0].rx()
|
||||
for num in range(0, self.rows):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# 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())
|
||||
self.add_power_pin(name=pin_name,
|
||||
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
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(pre, "vdd")
|
||||
|
|
|
|||
|
|
@ -32,61 +32,58 @@ class hierarchical_predecode(design.design):
|
|||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
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",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.add_nand(self.number_of_inputs)
|
||||
self.add_mod(self.nand)
|
||||
self.add_and(self.number_of_inputs)
|
||||
self.add_mod(self.and_mod)
|
||||
|
||||
def add_nand(self, inputs):
|
||||
def add_and(self, inputs):
|
||||
""" Create the NAND for the predecode input stage """
|
||||
if inputs==2:
|
||||
self.nand = factory.create(module_type="pnand2",
|
||||
self.and_mod = factory.create(module_type="pand2",
|
||||
height=self.cell_height)
|
||||
elif inputs==3:
|
||||
self.nand = factory.create(module_type="pnand3",
|
||||
self.and_mod = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
else:
|
||||
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
|
||||
|
||||
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
|
||||
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
|
||||
self.x_off_nand = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m2_pitch
|
||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||
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
|
||||
self.x_off_inv_2 = self.x_off_nand + self.nand.width
|
||||
|
||||
# Height width are computed
|
||||
self.width = self.x_off_inv_2 + self.inv.width
|
||||
self.width = self.x_off_and + self.and_mod.width
|
||||
|
||||
def route_rails(self):
|
||||
""" 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)]
|
||||
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",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
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)]
|
||||
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
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",
|
||||
pitch=self.m2_pitch,
|
||||
offset=offset,
|
||||
names=decode_names,
|
||||
length=self.height - 2*self.m1_width)
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
|
||||
def create_input_inverters(self):
|
||||
""" 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,
|
||||
mirror=mirror)
|
||||
|
||||
def create_output_inverters(self):
|
||||
""" Create inverters for the inverted output decode signals. """
|
||||
self.inv_inst = []
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
name = "pre_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 create_and_array(self, connections):
|
||||
""" Create the AND stage for the decodes """
|
||||
self.and_inst = []
|
||||
for and_input in range(self.number_of_outputs):
|
||||
inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
|
||||
name = "Xpre{0}_and_{1}".format(inout, and_input)
|
||||
self.and_inst.append(self.add_inst(name=name,
|
||||
mod=self.and_mod))
|
||||
self.connect_inst(connections[and_input])
|
||||
|
||||
|
||||
def place_output_inverters(self):
|
||||
""" Place inverters for the inverted output decode signals. """
|
||||
for inv_num in range(self.number_of_outputs):
|
||||
if (inv_num % 2 == 0):
|
||||
y_off = inv_num * self.inv.height
|
||||
def place_and_array(self):
|
||||
""" Place the AND stage for the decodes """
|
||||
for and_input in range(self.number_of_outputs):
|
||||
# inout = str(self.number_of_inputs) + "x" + str(self.number_of_outputs)
|
||||
if (and_input % 2 == 0):
|
||||
y_off = and_input * self.and_mod.height
|
||||
mirror = "R0"
|
||||
else:
|
||||
y_off =(inv_num + 1)*self.inv.height
|
||||
y_off = (and_input + 1) * self.and_mod.height
|
||||
mirror = "MX"
|
||||
offset = vector(self.x_off_inv_2, y_off)
|
||||
self.inv_inst[inv_num].place(offset=offset,
|
||||
offset = vector(self.x_off_and, y_off)
|
||||
self.and_inst[and_input].place(offset=offset,
|
||||
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):
|
||||
self.route_input_inverters()
|
||||
self.route_inputs_to_rails()
|
||||
self.route_nand_to_rails()
|
||||
self.route_output_inverters()
|
||||
self.route_and_to_rails()
|
||||
self.route_output_and()
|
||||
self.route_vdd_gnd()
|
||||
|
||||
def route_inputs_to_rails(self):
|
||||
|
|
@ -175,40 +145,31 @@ class hierarchical_predecode(design.design):
|
|||
for num in range(self.number_of_inputs):
|
||||
# route one signal next to each vdd/gnd rail since this is
|
||||
# typically where the p/n devices are and there are no
|
||||
# pins in the nand gates.
|
||||
y_offset = (num+self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
|
||||
# pins in the and gates.
|
||||
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space
|
||||
in_pin = "in_{}".format(num)
|
||||
a_pin = "A_{}".format(num)
|
||||
in_pos = vector(self.input_rails[in_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_via_center(layers = self.m1_stack,
|
||||
in_pos = vector(self.input_rails[in_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_via_center(layers=self.m1_stack,
|
||||
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])
|
||||
|
||||
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):
|
||||
|
||||
# route nand output to output inv input
|
||||
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")
|
||||
z_pin = self.and_inst[num].get_pin("Z")
|
||||
self.add_layout_pin(text="out_{}".format(num),
|
||||
layer="m1",
|
||||
offset=z_pin.ll(),
|
||||
height=z_pin.height(),
|
||||
width=z_pin.width())
|
||||
|
||||
|
||||
def route_input_inverters(self):
|
||||
"""
|
||||
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
|
||||
|
|
@ -219,7 +180,7 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
#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
|
||||
# pins in the nand gates.
|
||||
# pins in the and gates.
|
||||
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space
|
||||
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)
|
||||
|
|
@ -236,13 +197,12 @@ class hierarchical_predecode(design.design):
|
|||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=in_pos)
|
||||
|
||||
|
||||
def route_nand_to_rails(self):
|
||||
def route_and_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
nand_input_line_combination = self.get_nand_input_line_combination()
|
||||
and_input_line_combination = self.get_and_input_line_combination()
|
||||
for k in range(self.number_of_outputs):
|
||||
# create x offset list
|
||||
index_lst= nand_input_line_combination[k]
|
||||
index_lst= and_input_line_combination[k]
|
||||
|
||||
if self.number_of_inputs == 2:
|
||||
gate_lst = ["A","B"]
|
||||
|
|
@ -251,35 +211,32 @@ class hierarchical_predecode(design.design):
|
|||
|
||||
# this will connect pins A,B or A,B,C
|
||||
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)
|
||||
self.add_path("m1", [rail_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=rail_pos)
|
||||
|
||||
|
||||
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" 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
|
||||
in_xoffset = self.in_inst[0].rx() + self.m1_space
|
||||
out_xoffset = self.inv_inst[0].lx() - self.m1_space
|
||||
for num in range(0,self.number_of_outputs):
|
||||
# out_xoffset = self.and_inst[0].cx() + self.m1_space
|
||||
for num in range(0, self.number_of_outputs):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# Route both supplies
|
||||
for n in ["vdd", "gnd"]:
|
||||
nand_pin = self.nand_inst[num].get_pin(n)
|
||||
supply_offset = nand_pin.ll().scale(0,1)
|
||||
and_pin = self.and_inst[num].get_pin(n)
|
||||
supply_offset = and_pin.ll().scale(0, 1)
|
||||
self.add_rect(layer="m1",
|
||||
offset=supply_offset,
|
||||
width=self.inv_inst[num].rx())
|
||||
width=self.and_inst[num].rx())
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [in_xoffset, out_xoffset]:
|
||||
pin_pos = vector(xoffset, nand_pin.cy())
|
||||
for xoffset in [in_xoffset]:
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.add_power_pin(n, pin_pos)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,31 +27,29 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
|||
self.add_pins()
|
||||
self.add_modules()
|
||||
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)
|
||||
connections =[["inbar_0", "inbar_1", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "out_3", "vdd", "gnd"]]
|
||||
self.create_and_array(connections)
|
||||
|
||||
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
|
||||
4) a set of AND gates for inversion
|
||||
"""
|
||||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def get_nand_input_line_combination(self):
|
||||
""" These are the decoder connections of the NAND gates to the A,B pins """
|
||||
def get_and_input_line_combination(self):
|
||||
""" These are the decoder connections of the AND gates to the A,B pins """
|
||||
combination = [["Abar_0", "Abar_1"],
|
||||
["A_0", "Abar_1"],
|
||||
["Abar_0", "A_1"],
|
||||
|
|
|
|||
|
|
@ -27,16 +27,15 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
self.add_pins()
|
||||
self.add_modules()
|
||||
self.create_input_inverters()
|
||||
self.create_output_inverters()
|
||||
connections=[["inbar_0", "inbar_1", "inbar_2", "Z_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "Z_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "Z_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "Z_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "Z_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "Z_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "Z_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "Z_7", "vdd", "gnd"]]
|
||||
self.create_nand_array(connections)
|
||||
connections=[["inbar_0", "inbar_1", "inbar_2", "out_0", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "inbar_2", "out_1", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "inbar_2", "out_2", "vdd", "gnd"],
|
||||
["in_0", "in_1", "inbar_2", "out_3", "vdd", "gnd"],
|
||||
["inbar_0", "inbar_1", "in_2", "out_4", "vdd", "gnd"],
|
||||
["in_0", "inbar_1", "in_2", "out_5", "vdd", "gnd"],
|
||||
["inbar_0", "in_1", "in_2", "out_6", "vdd", "gnd"],
|
||||
["in_0", "in_1", "in_2", "out_7", "vdd", "gnd"]]
|
||||
self.create_and_array(connections)
|
||||
|
||||
def create_layout(self):
|
||||
"""
|
||||
|
|
@ -49,13 +48,12 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
|||
self.setup_layout_constraints()
|
||||
self.route_rails()
|
||||
self.place_input_inverters()
|
||||
self.place_output_inverters()
|
||||
self.place_nand_array()
|
||||
self.place_and_array()
|
||||
self.route()
|
||||
self.add_boundary()
|
||||
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 """
|
||||
combination = [["Abar_0", "Abar_1", "Abar_2"],
|
||||
["A_0", "Abar_1", "Abar_2"],
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ class pnand3(pgate.pgate):
|
|||
def route_inputs(self):
|
||||
""" 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
|
||||
self.inputB_yoffset = self.nwell_y_offset
|
||||
self.route_input_gate(self.pmos2_inst,
|
||||
|
|
@ -193,21 +194,20 @@ class pnand3(pgate.pgate):
|
|||
"B",
|
||||
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.nmos3_inst,
|
||||
self.inputC_yoffset,
|
||||
"C",
|
||||
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.nmos1_inst,
|
||||
self.inputA_yoffset,
|
||||
"A",
|
||||
position="center")
|
||||
|
||||
|
||||
def route_output(self):
|
||||
""" Route the Z output """
|
||||
# PMOS1 drain
|
||||
|
|
|
|||
Loading…
Reference in New Issue