mirror of https://github.com/VLSIDA/OpenRAM.git
First pass of multiple bitcells per decoder row
This commit is contained in:
parent
7888e54fc4
commit
2e67d44cd7
|
|
@ -32,11 +32,11 @@ class hierarchical_decoder(design.design):
|
||||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
self.cell_multiple = 1
|
self.cell_multiple = 1
|
||||||
|
# For debugging
|
||||||
|
# self.cell_multiple = 2
|
||||||
self.cell_height = self.cell_multiple * b.height
|
self.cell_height = self.cell_multiple * b.height
|
||||||
|
|
||||||
self.num_outputs = num_outputs
|
self.num_outputs = num_outputs
|
||||||
# We may have more than one bitcell per decoder row
|
|
||||||
self.rows = math.ceil(num_outputs / self.cell_multiple)
|
|
||||||
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
||||||
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||||
|
|
||||||
|
|
@ -52,6 +52,10 @@ class hierarchical_decoder(design.design):
|
||||||
"""
|
"""
|
||||||
b = factory.create(module_type="bitcell")
|
b = factory.create(module_type="bitcell")
|
||||||
|
|
||||||
|
# Old behavior
|
||||||
|
if OPTS.netlist_only:
|
||||||
|
return (b.height, 1)
|
||||||
|
|
||||||
# Search for the smallest multiple that works
|
# Search for the smallest multiple that works
|
||||||
cell_multiple = 1
|
cell_multiple = 1
|
||||||
while cell_multiple < 5:
|
while cell_multiple < 5:
|
||||||
|
|
@ -86,8 +90,8 @@ class hierarchical_decoder(design.design):
|
||||||
self.setup_layout_constants()
|
self.setup_layout_constants()
|
||||||
self.place_pre_decoder()
|
self.place_pre_decoder()
|
||||||
self.place_row_decoder()
|
self.place_row_decoder()
|
||||||
self.route_input_rails()
|
self.route_inputs()
|
||||||
self.route_predecode_rails()
|
self.route_decoder_bus()
|
||||||
self.route_vdd_gnd()
|
self.route_vdd_gnd()
|
||||||
self.offset_all_coordinates()
|
self.offset_all_coordinates()
|
||||||
self.add_boundary()
|
self.add_boundary()
|
||||||
|
|
@ -141,7 +145,7 @@ class hierarchical_decoder(design.design):
|
||||||
def setup_netlist_constants(self):
|
def setup_netlist_constants(self):
|
||||||
self.predec_groups = [] # This array is a 2D array.
|
self.predec_groups = [] # This array is a 2D array.
|
||||||
|
|
||||||
# Distributing vertical rails to different groups. One group belongs to one pre-decoder.
|
# Distributing vertical bus to different groups. One group belongs to one pre-decoder.
|
||||||
# For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will
|
# For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will
|
||||||
# have total 16 output lines out of these 3 pre-decoders and they will
|
# have total 16 output lines out of these 3 pre-decoders and they will
|
||||||
# be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
|
# be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
|
||||||
|
|
@ -180,35 +184,41 @@ class hierarchical_decoder(design.design):
|
||||||
|
|
||||||
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
|
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
|
||||||
|
|
||||||
|
# We may have more than one bitcell per decoder row
|
||||||
|
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
|
||||||
|
# We will place this many final decoders per row
|
||||||
|
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
|
||||||
|
|
||||||
# Calculates height and width of row-decoder
|
# Calculates height and width of row-decoder
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
nand_width = self.and2.width
|
nand_width = self.and2.width
|
||||||
else:
|
else:
|
||||||
nand_width = self.and3.width
|
nand_width = self.and3.width
|
||||||
self.internal_routing_width = self.m2_pitch * self.total_number_of_predecoder_outputs
|
self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1)
|
||||||
self.row_decoder_height = self.inv.height * self.rows
|
self.row_decoder_height = self.inv.height * self.num_rows
|
||||||
|
|
||||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
||||||
# Calculates height and width of hierarchical decoder
|
# Calculates height and width of hierarchical decoder
|
||||||
self.height = self.row_decoder_height
|
self.height = max(self.predecoder_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 \
|
||||||
|
+ self.decoders_per_row * nand_width + self.inv.width
|
||||||
|
|
||||||
def route_input_rails(self):
|
def route_inputs(self):
|
||||||
""" Create input rails for the predecoders """
|
""" Create input bus for the predecoders """
|
||||||
# inputs should be as high as the decoders
|
# inputs should be as high as the decoders
|
||||||
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
|
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
|
||||||
|
|
||||||
# Find the left-most predecoder
|
# Find the left-most predecoder
|
||||||
min_x = 0
|
min_x = 0
|
||||||
if self.no_of_pre2x4 > 0:
|
if self.no_of_pre2x4 > 0:
|
||||||
min_x = min(min_x, -self.pre2_4.width)
|
min_x = min(min_x, self.pre2x4_inst[0].lx())
|
||||||
if self.no_of_pre3x8 > 0:
|
if self.no_of_pre3x8 > 0:
|
||||||
min_x = min(min_x, -self.pre3_8.width)
|
min_x = min(min_x, self.pre3x8_inst[0].lx())
|
||||||
input_offset=vector(min_x - self.input_routing_width, 0)
|
input_offset=vector(min_x - self.input_routing_width, 0)
|
||||||
|
|
||||||
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
||||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
self.input_bus = self.create_vertical_pin_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
pitch=self.m2_pitch,
|
||||||
offset=input_offset,
|
offset=input_offset,
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
|
|
@ -222,7 +232,7 @@ class hierarchical_decoder(design.design):
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
index = pre_num * 2 + i
|
index = pre_num * 2 + i
|
||||||
|
|
||||||
input_pos = self.input_rails["addr_{}".format(index)]
|
input_pos = self.input_bus["addr_{}".format(index)]
|
||||||
|
|
||||||
in_name = "in_{}".format(i)
|
in_name = "in_{}".format(i)
|
||||||
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
||||||
|
|
@ -232,13 +242,13 @@ class hierarchical_decoder(design.design):
|
||||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_rail(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
||||||
for pre_num in range(self.no_of_pre3x8):
|
for pre_num in range(self.no_of_pre3x8):
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
||||||
|
|
||||||
input_pos = self.input_rails["addr_{}".format(index)]
|
input_pos = self.input_bus["addr_{}".format(index)]
|
||||||
|
|
||||||
in_name = "in_{}".format(i)
|
in_name = "in_{}".format(i)
|
||||||
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
||||||
|
|
@ -248,9 +258,9 @@ class hierarchical_decoder(design.design):
|
||||||
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
|
||||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||||
|
|
||||||
self.route_input_rail(decoder_offset, input_offset)
|
self.route_input_bus(decoder_offset, input_offset)
|
||||||
|
|
||||||
def route_input_rail(self, input_offset, output_offset):
|
def route_input_bus(self, input_offset, output_offset):
|
||||||
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
|
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
|
||||||
|
|
||||||
self.add_via_center(layers=self.m2_stack,
|
self.add_via_center(layers=self.m2_stack,
|
||||||
|
|
@ -334,18 +344,17 @@ class hierarchical_decoder(design.design):
|
||||||
else:
|
else:
|
||||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||||
|
|
||||||
self.pre2x4_inst[num].place(base)
|
self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
|
||||||
|
|
||||||
def place_pre3x8(self, num):
|
def place_pre3x8(self, num):
|
||||||
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||||
if (self.num_inputs == 3):
|
if (self.num_inputs == 3):
|
||||||
offset = vector(-self.pre_3_8.width, 0)
|
offset = vector(-self.pre_3_8.width, 0)
|
||||||
mirror = "R0"
|
|
||||||
else:
|
else:
|
||||||
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
||||||
offset = vector(-self.pre3_8.width, height)
|
offset = vector(-self.pre3_8.width, height)
|
||||||
|
|
||||||
self.pre3x8_inst[num].place(offset)
|
self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
|
||||||
|
|
||||||
def create_row_decoder(self):
|
def create_row_decoder(self):
|
||||||
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
||||||
|
|
@ -362,14 +371,14 @@ class hierarchical_decoder(design.design):
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for i in range(len(self.predec_groups[0])):
|
for i in range(len(self.predec_groups[0])):
|
||||||
for j in range(len(self.predec_groups[1])):
|
for j in range(len(self.predec_groups[1])):
|
||||||
row = len(self.predec_groups[0]) * j + i
|
output = len(self.predec_groups[0]) * j + i
|
||||||
if (row < self.num_outputs):
|
if (output < self.num_outputs):
|
||||||
name = self.AND_FORMAT.format(row)
|
name = self.AND_FORMAT.format(output)
|
||||||
self.and_inst.append(self.add_inst(name=name,
|
self.and_inst.append(self.add_inst(name=name,
|
||||||
mod=self.and2))
|
mod=self.and2))
|
||||||
pins =["out_{0}".format(i),
|
pins =["out_{0}".format(i),
|
||||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||||
"decode_{0}".format(row),
|
"decode_{0}".format(output),
|
||||||
"vdd", "gnd"]
|
"vdd", "gnd"]
|
||||||
self.connect_inst(pins)
|
self.connect_inst(pins)
|
||||||
|
|
||||||
|
|
@ -378,18 +387,18 @@ class hierarchical_decoder(design.design):
|
||||||
for i in range(len(self.predec_groups[0])):
|
for i in range(len(self.predec_groups[0])):
|
||||||
for j in range(len(self.predec_groups[1])):
|
for j in range(len(self.predec_groups[1])):
|
||||||
for k in range(len(self.predec_groups[2])):
|
for k in range(len(self.predec_groups[2])):
|
||||||
row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
|
output = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
|
||||||
+ len(self.predec_groups[0]) * j + i
|
+ len(self.predec_groups[0]) * j + i
|
||||||
|
|
||||||
if (row < self.num_outputs):
|
if (output < self.num_outputs):
|
||||||
name = self.AND_FORMAT.format(row)
|
name = self.AND_FORMAT.format(output)
|
||||||
self.and_inst.append(self.add_inst(name=name,
|
self.and_inst.append(self.add_inst(name=name,
|
||||||
mod=self.and3))
|
mod=self.and3))
|
||||||
|
|
||||||
pins = ["out_{0}".format(i),
|
pins = ["out_{0}".format(i),
|
||||||
"out_{0}".format(j + len(self.predec_groups[0])),
|
"out_{0}".format(j + len(self.predec_groups[0])),
|
||||||
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
|
||||||
"decode_{0}".format(row),
|
"decode_{0}".format(output),
|
||||||
"vdd", "gnd"]
|
"vdd", "gnd"]
|
||||||
self.connect_inst(pins)
|
self.connect_inst(pins)
|
||||||
|
|
||||||
|
|
@ -403,7 +412,10 @@ class hierarchical_decoder(design.design):
|
||||||
self.route_decoder()
|
self.route_decoder()
|
||||||
|
|
||||||
def place_decoder_and_array(self):
|
def place_decoder_and_array(self):
|
||||||
""" Add a column of AND gates for final decode """
|
"""
|
||||||
|
Add a column of AND gates for final decode.
|
||||||
|
This may have more than one decoder per row to match the bitcell height.
|
||||||
|
"""
|
||||||
|
|
||||||
# Row Decoder AND GATE array for address inputs <5.
|
# Row Decoder AND GATE array for address inputs <5.
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
|
|
@ -415,9 +427,13 @@ class hierarchical_decoder(design.design):
|
||||||
self.place_and_array(and_mod=self.and3)
|
self.place_and_array(and_mod=self.and3)
|
||||||
|
|
||||||
def place_and_array(self, and_mod):
|
def place_and_array(self, and_mod):
|
||||||
""" Add a column of AND gates for the decoder above the predecoders."""
|
"""
|
||||||
|
Add a column of AND gates for the decoder above the predecoders.
|
||||||
|
"""
|
||||||
|
|
||||||
for row in range(self.num_outputs):
|
for inst_index in range(self.num_outputs):
|
||||||
|
row = math.floor(inst_index / self.decoders_per_row)
|
||||||
|
dec = inst_index % self.decoders_per_row
|
||||||
if ((row % 2) == 0):
|
if ((row % 2) == 0):
|
||||||
y_off = and_mod.height * row
|
y_off = and_mod.height * row
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
|
|
@ -425,46 +441,52 @@ class hierarchical_decoder(design.design):
|
||||||
y_off = and_mod.height * (row + 1)
|
y_off = and_mod.height * (row + 1)
|
||||||
mirror = "MX"
|
mirror = "MX"
|
||||||
|
|
||||||
self.and_inst[row].place(offset=[self.internal_routing_width, y_off],
|
x_off = self.internal_routing_width + dec * and_mod.width
|
||||||
|
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
||||||
mirror=mirror)
|
mirror=mirror)
|
||||||
|
|
||||||
def route_decoder(self):
|
def route_decoder(self):
|
||||||
""" Add the pins. """
|
""" Add the pins. """
|
||||||
|
|
||||||
for row in range(self.num_outputs):
|
for output in range(self.num_outputs):
|
||||||
z_pin = self.and_inst[row].get_pin("Z")
|
z_pin = self.and_inst[output].get_pin("Z")
|
||||||
self.add_layout_pin(text="decode_{0}".format(row),
|
self.add_layout_pin(text="decode_{0}".format(output),
|
||||||
layer="m1",
|
layer="m1",
|
||||||
offset=z_pin.ll(),
|
offset=z_pin.ll(),
|
||||||
width=z_pin.width(),
|
width=z_pin.width(),
|
||||||
height=z_pin.height())
|
height=z_pin.height())
|
||||||
|
|
||||||
def route_predecode_rails(self):
|
def route_decoder_bus(self):
|
||||||
""" Creates vertical metal 2 rails to connect predecoder and decoder stages."""
|
"""
|
||||||
|
Creates vertical metal 2 bus to connect predecoder and decoder stages.
|
||||||
|
"""
|
||||||
|
|
||||||
# This is not needed for inputs <4 since they have no pre/decode stages.
|
# This is not needed for inputs <4 since they have no pre/decode stages.
|
||||||
if (self.num_inputs >= 4):
|
if (self.num_inputs >= 4):
|
||||||
input_offset = vector(0.5 * self.m2_width, 0)
|
# This leaves an offset for the predecoder output jogs
|
||||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||||
self.predecode_rails = self.create_vertical_pin_bus(layer="m2",
|
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
|
||||||
pitch=self.m2_pitch,
|
pitch=self.m2_pitch,
|
||||||
offset=input_offset,
|
offset=vector(0, 0),
|
||||||
names=input_bus_names,
|
names=input_bus_names,
|
||||||
length=self.height)
|
length=self.height)
|
||||||
|
|
||||||
self.route_rails_to_predecodes()
|
self.route_bus_to_predecodes()
|
||||||
self.route_rails_to_decoder()
|
self.route_bus_to_decoder()
|
||||||
|
|
||||||
def route_rails_to_predecodes(self):
|
|
||||||
""" Iterates through all of the predecodes and connects to the rails including the offsets """
|
|
||||||
|
|
||||||
|
def route_bus_to_predecodes(self):
|
||||||
|
"""
|
||||||
|
Iterates through all of the predecodes
|
||||||
|
and connects to the rails including the offsets
|
||||||
|
"""
|
||||||
# FIXME: convert to connect_bus
|
# FIXME: convert to connect_bus
|
||||||
for pre_num in range(self.no_of_pre2x4):
|
for pre_num in range(self.no_of_pre2x4):
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
||||||
out_name = "out_{}".format(i)
|
out_name = "out_{}".format(i)
|
||||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||||
self.route_predecode_rail_m3(predecode_name, pin)
|
x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
|
||||||
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
||||||
|
|
||||||
# FIXME: convert to connect_bus
|
# FIXME: convert to connect_bus
|
||||||
for pre_num in range(self.no_of_pre3x8):
|
for pre_num in range(self.no_of_pre3x8):
|
||||||
|
|
@ -472,44 +494,68 @@ class hierarchical_decoder(design.design):
|
||||||
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||||
out_name = "out_{}".format(i)
|
out_name = "out_{}".format(i)
|
||||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||||
self.route_predecode_rail_m3(predecode_name, pin)
|
x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
|
||||||
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
||||||
|
|
||||||
def route_rails_to_decoder(self):
|
def route_bus_to_decoder(self):
|
||||||
""" Use the self.predec_groups to determine the connections to the decoder AND gates.
|
|
||||||
Inputs of AND2/AND3 gates come from different groups.
|
|
||||||
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
|
|
||||||
[8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
|
|
||||||
[0,4,8] and second AND3 is connected to [0,4,9] ........... and the
|
|
||||||
128th AND3 is connected to [3,7,15]
|
|
||||||
"""
|
"""
|
||||||
row_index = 0
|
Use the self.predec_groups to determine the connections to the decoder AND gates.
|
||||||
|
Inputs of AND2/AND3 gates come from different groups.
|
||||||
|
For example for these groups
|
||||||
|
[ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
|
||||||
|
the first AND3 inputs are connected to [0,4,8],
|
||||||
|
second AND3 is connected to [0,4,9],
|
||||||
|
...
|
||||||
|
and the 128th AND3 is connected to [3,7,15]
|
||||||
|
"""
|
||||||
|
output_index = 0
|
||||||
|
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for index_B in self.predec_groups[1]:
|
for index_B in self.predec_groups[1]:
|
||||||
for index_A in self.predec_groups[0]:
|
for index_A in self.predec_groups[0]:
|
||||||
# FIXME: convert to connect_bus?
|
# FIXME: convert to connect_bus?
|
||||||
if (row_index < self.num_outputs):
|
if (output_index < self.num_outputs):
|
||||||
|
row_index = math.floor(output_index / self.decoders_per_row)
|
||||||
|
row_remainder = (output_index % self.decoders_per_row)
|
||||||
|
row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 4) * self.m1_pitch
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
|
self.and_inst[output_index].get_pin("A"),
|
||||||
|
row_offset)
|
||||||
predecode_name = "predecode_{}".format(index_B)
|
predecode_name = "predecode_{}".format(index_B)
|
||||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
row_index = row_index + 1
|
self.and_inst[output_index].get_pin("B"),
|
||||||
|
row_offset + self.m1_pitch)
|
||||||
|
output_index = output_index + 1
|
||||||
|
|
||||||
elif (self.num_inputs > 5):
|
elif (self.num_inputs > 5):
|
||||||
for index_C in self.predec_groups[2]:
|
for index_C in self.predec_groups[2]:
|
||||||
for index_B in self.predec_groups[1]:
|
for index_B in self.predec_groups[1]:
|
||||||
for index_A in self.predec_groups[0]:
|
for index_A in self.predec_groups[0]:
|
||||||
# FIXME: convert to connect_bus?
|
# FIXME: convert to connect_bus?
|
||||||
if (row_index < self.num_outputs):
|
if (output_index < self.num_outputs):
|
||||||
|
row_index = math.floor(output_index / self.decoders_per_row)
|
||||||
|
row_remainder = (output_index % self.decoders_per_row)
|
||||||
|
row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 4) * self.m1_pitch
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("A"))
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
|
self.and_inst[output_index].get_pin("A"),
|
||||||
|
row_offset)
|
||||||
predecode_name = "predecode_{}".format(index_B)
|
predecode_name = "predecode_{}".format(index_B)
|
||||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B"))
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
|
self.and_inst[output_index].get_pin("B"),
|
||||||
|
row_offset + self.m1_pitch)
|
||||||
predecode_name = "predecode_{}".format(index_C)
|
predecode_name = "predecode_{}".format(index_C)
|
||||||
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C"))
|
self.route_predecode_bus_outputs(predecode_name,
|
||||||
row_index = row_index + 1
|
self.and_inst[output_index].get_pin("C"),
|
||||||
|
row_offset + 2 * self.m1_pitch)
|
||||||
|
output_index = output_index + 1
|
||||||
|
|
||||||
def route_vdd_gnd(self):
|
def route_vdd_gnd(self):
|
||||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
"""
|
||||||
|
Add a pin for each row of vdd/gnd which are
|
||||||
|
must-connects next level up.
|
||||||
|
"""
|
||||||
|
|
||||||
# The vias will be placed in the center and right of the cells, respectively.
|
# The vias will be placed in the center and right of the cells, respectively.
|
||||||
xoffset = self.and_inst[0].rx()
|
xoffset = self.and_inst[0].rx()
|
||||||
|
|
@ -526,23 +572,42 @@ class hierarchical_decoder(design.design):
|
||||||
self.copy_layout_pin(pre, "vdd")
|
self.copy_layout_pin(pre, "vdd")
|
||||||
self.copy_layout_pin(pre, "gnd")
|
self.copy_layout_pin(pre, "gnd")
|
||||||
|
|
||||||
def route_predecode_rail(self, rail_name, pin):
|
def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
|
||||||
""" Connect the routing rail to the given metal1 pin """
|
"""
|
||||||
rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y)
|
Connect the routing rail to the given metal1 pin
|
||||||
self.add_path("m1", [rail_pos, pin.lc()])
|
using a routing track at the given y_offset
|
||||||
|
|
||||||
|
"""
|
||||||
|
pin_pos = pin.center()
|
||||||
|
# If we have a single decoder per row, we can route on M1
|
||||||
|
if self.decoders_per_row == 1:
|
||||||
|
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||||
|
self.add_path("m1", [rail_pos, pin_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=rail_pos)
|
offset=rail_pos)
|
||||||
|
# If not, we must route over the decoder cells on M3
|
||||||
|
else:
|
||||||
|
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
||||||
|
mid_pos = vector(pin_pos.x, rail_pos.y)
|
||||||
|
self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos])
|
||||||
|
self.add_via_center(layers=self.m2_stack,
|
||||||
|
offset=rail_pos)
|
||||||
|
self.add_via_center(layers=self.m1_stack,
|
||||||
|
offset=pin_pos)
|
||||||
|
|
||||||
def route_predecode_rail_m3(self, rail_name, pin):
|
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
||||||
""" Connect the routing rail to the given metal1 pin """
|
"""
|
||||||
|
Connect the routing rail to the given metal1 pin using a jog
|
||||||
|
to the right of the cell at the given x_offset.
|
||||||
|
"""
|
||||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||||
# It would be fixed with a channel router.
|
# It would be fixed with a channel router.
|
||||||
mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2)
|
pin_pos = pin.center()
|
||||||
rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y)
|
mid_point1 = vector(x_offset, pin_pos.y)
|
||||||
|
mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
|
||||||
|
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||||
|
self.add_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
offset=pin.center())
|
|
||||||
self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()])
|
|
||||||
self.add_via_center(layers=self.m2_stack,
|
|
||||||
offset=rail_pos)
|
offset=rail_pos)
|
||||||
|
|
||||||
def input_load(self):
|
def input_load(self):
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ class pnand3(pgate.pgate):
|
||||||
self.nmos3_inst,
|
self.nmos3_inst,
|
||||||
self.inputC_yoffset,
|
self.inputC_yoffset,
|
||||||
"C",
|
"C",
|
||||||
position="center")
|
position="left")
|
||||||
|
|
||||||
# FIXME: constant hack
|
# FIXME: constant hack
|
||||||
if OPTS.tech_name == "s8":
|
if OPTS.tech_name == "s8":
|
||||||
|
|
@ -232,7 +232,7 @@ class pnand3(pgate.pgate):
|
||||||
self.nmos1_inst,
|
self.nmos1_inst,
|
||||||
self.inputA_yoffset,
|
self.inputA_yoffset,
|
||||||
"A",
|
"A",
|
||||||
position="center")
|
position="left")
|
||||||
|
|
||||||
def route_output(self):
|
def route_output(self):
|
||||||
""" Route the Z output """
|
""" Route the Z output """
|
||||||
|
|
@ -255,10 +255,10 @@ class pnand3(pgate.pgate):
|
||||||
directions=("V", "V"))
|
directions=("V", "V"))
|
||||||
|
|
||||||
# PMOS3 and NMOS3 are drain aligned
|
# PMOS3 and NMOS3 are drain aligned
|
||||||
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()])
|
self.add_path("m1", [pmos3_pin.center(), nmos3_pin.center()])
|
||||||
# Route in the A input track (top track)
|
# Route in the A input track (top track)
|
||||||
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
|
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
|
||||||
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
|
self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
|
||||||
|
|
||||||
# This extends the output to the edge of the cell
|
# This extends the output to the edge of the cell
|
||||||
self.add_via_center(layers=self.m1_stack,
|
self.add_via_center(layers=self.m1_stack,
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,6 @@ class hierarchical_decoder_test(openram_test):
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
||||||
globals.init_openram(config_file)
|
globals.init_openram(config_file)
|
||||||
# Doesn't require hierarchical decoder
|
|
||||||
# debug.info(1, "Testing 4 row sample for hierarchical_decoder")
|
|
||||||
# a = hierarchical_decoder.hierarchical_decoder(name="hd1, num_outputs=4)
|
|
||||||
# self.local_check(a)
|
|
||||||
|
|
||||||
# Doesn't require hierarchical decoder
|
|
||||||
# debug.info(1, "Testing 8 row sample for hierarchical_decoder")
|
|
||||||
# a = hierarchical_decoder.hierarchical_decoder(name="hd2", num_outputs=8)
|
|
||||||
# self.local_check(a)
|
|
||||||
|
|
||||||
# check hierarchical decoder for single port
|
# check hierarchical decoder for single port
|
||||||
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue