mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-08-31 18:25:18 +02:00
Thin-cell decoder changes.
Add hard decoder gates (nand, inv, pnan) Add conditions for routing using LI layer in s8. Generalize bus layers for decoders. Move custom cells to own directory. Fixed via directions, etc. Add 4x16 hierarchical decoder and test case
This commit is contained in:
@@ -12,7 +12,6 @@ from sram_factory import factory
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from errors import drc_error
|
||||
from tech import cell_properties, layer
|
||||
|
||||
|
||||
class hierarchical_decoder(design.design):
|
||||
@@ -28,12 +27,8 @@ class hierarchical_decoder(design.design):
|
||||
self.pre3x8_inst = []
|
||||
|
||||
b = factory.create(module_type="bitcell")
|
||||
try:
|
||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||
except AttributeError:
|
||||
self.cell_multiple = 1
|
||||
self.cell_height = self.cell_multiple * b.height
|
||||
|
||||
self.cell_height = b.height
|
||||
|
||||
self.num_outputs = num_outputs
|
||||
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
||||
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||
@@ -41,41 +36,6 @@ class hierarchical_decoder(design.design):
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
def find_decoder_height(self):
|
||||
"""
|
||||
Dead code. This would dynamically determine the bitcell multiple,
|
||||
but I just decided to hard code it in the tech file if it is not 1
|
||||
because a DRC tool would be required even to run in front-end mode.
|
||||
"""
|
||||
b = factory.create(module_type="bitcell")
|
||||
|
||||
# Old behavior
|
||||
if OPTS.netlist_only:
|
||||
return (b.height, 1)
|
||||
|
||||
# Search for the smallest multiple that works
|
||||
cell_multiple = 1
|
||||
while cell_multiple < 5:
|
||||
cell_height = cell_multiple * b.height
|
||||
# debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height))
|
||||
try:
|
||||
and3 = factory.create(module_type="pand3",
|
||||
height=cell_height)
|
||||
except drc_error:
|
||||
# debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height))
|
||||
pass
|
||||
else:
|
||||
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True)
|
||||
total_errors = drc_errors + lvs_errors
|
||||
if total_errors == 0:
|
||||
debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple))
|
||||
return (cell_height, cell_multiple)
|
||||
|
||||
cell_multiple += 1
|
||||
|
||||
else:
|
||||
debug.error("Couldn't find a valid decoder height multiple.", -1)
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
@@ -97,15 +57,22 @@ class hierarchical_decoder(design.design):
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_modules(self):
|
||||
self.inv = factory.create(module_type="pinv",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.inv)
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
height=self.cell_height)
|
||||
if OPTS.tech_name == "s8":
|
||||
self.and2 = factory.create(module_type="pand2_dec")
|
||||
else:
|
||||
self.and2 = factory.create(module_type="pand2_dec",
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and2)
|
||||
self.and3 = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
if OPTS.tech_name == "s8":
|
||||
self.and3 = factory.create(module_type="pand3_dec")
|
||||
else:
|
||||
self.and3 = factory.create(module_type="pand3_dec",
|
||||
height=self.cell_height)
|
||||
|
||||
self.add_mod(self.and3)
|
||||
# TBD
|
||||
# self.and4 = factory.create(module_type="pand4_dec")
|
||||
# self.add_mod(self.and4)
|
||||
|
||||
self.add_decoders()
|
||||
|
||||
@@ -176,56 +143,63 @@ class hierarchical_decoder(design.design):
|
||||
-1)
|
||||
|
||||
# Calculates height and width of pre-decoder,
|
||||
if self.no_of_pre3x8 > 0:
|
||||
# FIXME: Update with 4x16
|
||||
if self.no_of_pre3x8 > 0 and self.no_of_pre2x4 > 0:
|
||||
self.predecoder_width = max(self.pre3_8.width, self.pre2_4.width)
|
||||
elif self.no_of_pre3x8 > 0:
|
||||
self.predecoder_width = self.pre3_8.width
|
||||
else:
|
||||
self.predecoder_width = self.pre2_4.width
|
||||
|
||||
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)
|
||||
# We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row
|
||||
if self.decoders_per_row == 1:
|
||||
self.decoder_bus_pitch = self.m2_pitch
|
||||
elif self.decoders_per_row == 2:
|
||||
self.decoder_bus_pitch = self.m3_pitch
|
||||
# How much space between each predecoder
|
||||
self.predecoder_spacing = self.and2.height
|
||||
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 \
|
||||
+ (self.no_of_pre2x4 + self.no_of_pre3x8 - 1) * self.predecoder_spacing
|
||||
|
||||
# Inputs to cells are on input layer
|
||||
# Outputs from cells are on output layer
|
||||
if OPTS.tech_name == "s8":
|
||||
self.bus_layer = "m1"
|
||||
self.bus_directions = "nonpref"
|
||||
self.bus_pitch = self.m1_pitch
|
||||
self.bus_space = self.m2_space
|
||||
self.input_layer = "m2"
|
||||
self.output_layer = "li"
|
||||
self.output_layer_pitch = self.li_pitch
|
||||
else:
|
||||
debug.error("Insufficient layers for multi-bit height decoder.", -1)
|
||||
self.bus_layer = "m2"
|
||||
self.bus_directions = "pref"
|
||||
self.bus_pitch = self.m2_pitch
|
||||
self.bus_space = self.m2_space
|
||||
# These two layers being the same requires a special jog
|
||||
# to ensure to conflicts with the output layers
|
||||
self.input_layer = "m1"
|
||||
self.output_layer = "m3"
|
||||
self.output_layer_pitch = self.m3_pitch
|
||||
|
||||
# Two extra pitches between modules on left and right
|
||||
self.internal_routing_width = self.total_number_of_predecoder_outputs * self.bus_pitch + self.bus_pitch
|
||||
self.row_decoder_height = self.and2.height * self.num_outputs
|
||||
# Extra bus space for supply contacts
|
||||
self.input_routing_width = self.num_inputs * self.bus_pitch + self.bus_space
|
||||
|
||||
# Calculates height and width of row-decoder
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
nand_width = self.and2.width
|
||||
nand_inputs = 2
|
||||
else:
|
||||
nand_width = self.and3.width
|
||||
nand_inputs = 3
|
||||
self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1)
|
||||
self.row_decoder_height = self.inv.height * self.num_rows
|
||||
|
||||
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch
|
||||
# print(self.decoders_per_row, nand_inputs)
|
||||
# print(decoder_input_wire_height, self.cell_height)
|
||||
if decoder_input_wire_height > self.cell_height:
|
||||
debug.warning("Cannot fit multi-bit decoder routes per row.")
|
||||
# debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
|
||||
|
||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
||||
|
||||
# Calculates height and width of hierarchical decoder
|
||||
# Add extra pitch for good measure
|
||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch
|
||||
self.width = self.input_routing_width + self.predecoder_width \
|
||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.bus_space
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
self.nand_width = self.and2.width
|
||||
else:
|
||||
self.nand_width = self.and3.width
|
||||
|
||||
self.width = self.input_routing_width \
|
||||
+ self.predecoder_width \
|
||||
+ self.internal_routing_width \
|
||||
+ self.decoders_per_row * nand_width + self.inv.width
|
||||
|
||||
+ self.nand_width \
|
||||
+ self.m1_space
|
||||
|
||||
def route_inputs(self):
|
||||
""" Create input bus 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:
|
||||
@@ -235,10 +209,10 @@ class hierarchical_decoder(design.design):
|
||||
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_bus = self.create_vertical_pin_bus(layer="m2",
|
||||
self.input_bus = self.create_vertical_pin_bus(layer=self.bus_layer,
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=input_height)
|
||||
length=self.predecoder_height)
|
||||
|
||||
self.route_input_to_predecodes()
|
||||
|
||||
@@ -253,9 +227,7 @@ class hierarchical_decoder(design.design):
|
||||
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 + self.m1_pitch))
|
||||
decoder_offset = decoder_pin.center()
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_bus(decoder_offset, input_offset)
|
||||
@@ -269,9 +241,7 @@ class hierarchical_decoder(design.design):
|
||||
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 + self.m1_pitch))
|
||||
decoder_offset = decoder_pin.center()
|
||||
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
||||
|
||||
self.route_input_bus(decoder_offset, input_offset)
|
||||
@@ -282,13 +252,14 @@ class hierarchical_decoder(design.design):
|
||||
vertical M2 coordinate to the predecode inputs
|
||||
"""
|
||||
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer="m3",
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.input_layer,
|
||||
offset=input_offset)
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer="m3",
|
||||
offset=output_offset)
|
||||
self.add_path("m3", [input_offset, output_offset])
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.input_layer,
|
||||
offset=output_offset,
|
||||
directions=self.bus_directions)
|
||||
self.add_path(self.input_layer, [input_offset, output_offset])
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
@@ -363,19 +334,19 @@ class hierarchical_decoder(design.design):
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(-self.pre2_4.width, 0)
|
||||
else:
|
||||
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
||||
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
|
||||
|
||||
self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
|
||||
self.pre2x4_inst[num].place(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)
|
||||
else:
|
||||
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
||||
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing)
|
||||
offset = vector(-self.pre3_8.width, height)
|
||||
|
||||
self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
|
||||
self.pre3x8_inst[num].place(offset)
|
||||
|
||||
def create_row_decoder(self):
|
||||
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
||||
@@ -431,7 +402,6 @@ class hierarchical_decoder(design.design):
|
||||
if (self.num_inputs >= 4):
|
||||
self.place_decoder_and_array()
|
||||
|
||||
|
||||
def place_decoder_and_array(self):
|
||||
"""
|
||||
Add a column of AND gates for final decode.
|
||||
@@ -452,9 +422,7 @@ class hierarchical_decoder(design.design):
|
||||
Add a column of AND gates for the decoder above the predecoders.
|
||||
"""
|
||||
|
||||
for inst_index in range(self.num_outputs):
|
||||
row = math.floor(inst_index / self.decoders_per_row)
|
||||
dec = inst_index % self.decoders_per_row
|
||||
for row in range(self.num_outputs):
|
||||
if ((row % 2) == 0):
|
||||
y_off = and_mod.height * row
|
||||
mirror = "R0"
|
||||
@@ -462,32 +430,16 @@ class hierarchical_decoder(design.design):
|
||||
y_off = and_mod.height * (row + 1)
|
||||
mirror = "MX"
|
||||
|
||||
x_off = self.internal_routing_width + dec * and_mod.width
|
||||
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
||||
mirror=mirror)
|
||||
x_off = self.internal_routing_width
|
||||
self.and_inst[row].place(offset=vector(x_off, y_off),
|
||||
mirror=mirror)
|
||||
|
||||
def route_outputs(self):
|
||||
""" Add the pins. """
|
||||
|
||||
max_xoffset = max(x.rx() for x in self.and_inst)
|
||||
|
||||
for output_index in range(self.num_outputs):
|
||||
row_remainder = (output_index % self.decoders_per_row)
|
||||
|
||||
and_inst = self.and_inst[output_index]
|
||||
z_pin = and_inst.get_pin("Z")
|
||||
if row_remainder == 0 and self.decoders_per_row > 1:
|
||||
layer = "m3"
|
||||
self.add_via_stack_center(from_layer=z_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=z_pin.center())
|
||||
else:
|
||||
layer = z_pin.layer
|
||||
|
||||
self.add_layout_pin_segment_center(text="decode_{0}".format(output_index),
|
||||
layer=layer,
|
||||
start=z_pin.center(),
|
||||
end=vector(max_xoffset, z_pin.cy()))
|
||||
for row in range(self.num_outputs):
|
||||
and_inst = self.and_inst[row]
|
||||
self.copy_layout_pin(and_inst, "Z", "decode_{0}".format(row))
|
||||
|
||||
def route_decoder_bus(self):
|
||||
"""
|
||||
@@ -498,9 +450,9 @@ class hierarchical_decoder(design.design):
|
||||
if (self.num_inputs >= 4):
|
||||
# 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)]
|
||||
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.decoder_bus_pitch,
|
||||
offset=vector(0, 0),
|
||||
self.predecode_bus = self.create_vertical_pin_bus(layer=self.bus_layer,
|
||||
pitch=self.bus_pitch,
|
||||
offset=vector(self.bus_pitch, 0),
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
|
||||
@@ -518,8 +470,9 @@ class hierarchical_decoder(design.design):
|
||||
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
||||
x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
||||
x_offset = self.pre2x4_inst[pre_num].rx() + self.output_layer_pitch
|
||||
y_offset = self.pre2x4_inst[pre_num].by() + i * self.cell_height
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||
|
||||
# FIXME: convert to connect_bus
|
||||
for pre_num in range(self.no_of_pre3x8):
|
||||
@@ -527,8 +480,9 @@ class hierarchical_decoder(design.design):
|
||||
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
||||
out_name = "out_{}".format(i)
|
||||
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
||||
x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
||||
x_offset = self.pre3x8_inst[pre_num].rx() + self.output_layer_pitch
|
||||
y_offset = self.pre3x8_inst[pre_num].by() + i * self.cell_height
|
||||
self.route_predecode_bus_inputs(predecode_name, pin, x_offset, y_offset)
|
||||
|
||||
def route_bus_to_decoder(self):
|
||||
"""
|
||||
@@ -542,12 +496,6 @@ class hierarchical_decoder(design.design):
|
||||
and the 128th AND3 is connected to [3,7,15]
|
||||
"""
|
||||
output_index = 0
|
||||
|
||||
if "li" in layer:
|
||||
self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]]
|
||||
else:
|
||||
self.decoder_layers = [self.m2_stack[::-1]]
|
||||
debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.")
|
||||
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
for index_B in self.predec_groups[1]:
|
||||
@@ -557,13 +505,11 @@ class hierarchical_decoder(design.design):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("A"),
|
||||
output_index,
|
||||
0)
|
||||
output_index)
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("B"),
|
||||
output_index,
|
||||
1)
|
||||
output_index)
|
||||
output_index = output_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
@@ -575,18 +521,15 @@ class hierarchical_decoder(design.design):
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("A"),
|
||||
output_index,
|
||||
0)
|
||||
output_index)
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("B"),
|
||||
output_index,
|
||||
1)
|
||||
output_index)
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("C"),
|
||||
output_index,
|
||||
2)
|
||||
output_index)
|
||||
output_index = output_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
@@ -594,90 +537,90 @@ class hierarchical_decoder(design.design):
|
||||
Add a pin for each row of vdd/gnd which are
|
||||
must-connects next level up.
|
||||
"""
|
||||
|
||||
if OPTS.tech_name == "s8":
|
||||
for n in ["vdd", "gnd"]:
|
||||
pins = self.and_inst[0].get_pins(n)
|
||||
for pin in pins:
|
||||
self.add_rect(layer=pin.layer,
|
||||
offset=pin.ll() + vector(0, self.bus_space),
|
||||
width=pin.width(),
|
||||
height=self.height - 2 * self.bus_space)
|
||||
|
||||
# The vias will be placed at the right of the cells.
|
||||
xoffset = max(x.rx() for x in self.and_inst)
|
||||
for num in range(0, self.num_outputs):
|
||||
# Only add the power pin for the 1st in each row
|
||||
if num % self.decoders_per_row:
|
||||
continue
|
||||
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.and_inst[num].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_path(supply_pin.layer,
|
||||
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin_pos,
|
||||
start_layer=supply_pin.layer)
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(pre, "vdd")
|
||||
self.copy_layout_pin(pre, "gnd")
|
||||
# This adds power vias at the top of each cell
|
||||
# (except the last to keep them inside the boundary)
|
||||
for i in self.and_inst[:-1]:
|
||||
pins = i.get_pins(n)
|
||||
for pin in pins:
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin.uc(),
|
||||
start_layer=pin.layer)
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin.uc(),
|
||||
start_layer=pin.layer)
|
||||
|
||||
for i in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(i, n)
|
||||
else:
|
||||
# The vias will be placed at the right of the cells.
|
||||
xoffset = max(x.rx() for x in self.and_inst) + 0.5 * self.m1_space
|
||||
for row in range(0, self.num_outputs):
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.and_inst[row].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin_pos,
|
||||
start_layer=supply_pin.layer)
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
self.copy_layout_pin(pre, pin_name)
|
||||
|
||||
def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index):
|
||||
def route_predecode_bus_outputs(self, rail_name, pin, row):
|
||||
"""
|
||||
Connect the routing rail to the given metal1 pin
|
||||
using a routing track at the given y_offset
|
||||
"""
|
||||
|
||||
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
|
||||
|
||||
pin_pos = pin.center()
|
||||
|
||||
# y_offset is the same for both the M2 and M4 routes so that the rail
|
||||
# contacts align and don't cause problems
|
||||
if pin_index == 0:
|
||||
# Bottom pitch
|
||||
y_offset = row_offset
|
||||
elif pin_index == 1:
|
||||
# One pitch from top
|
||||
y_offset = row_offset + self.and_inst[0].height - self.m3_pitch
|
||||
elif pin_index == 2:
|
||||
# One pitch from bottom
|
||||
y_offset = row_offset + self.m3_pitch
|
||||
else:
|
||||
debug.error("Invalid decoder pitch.")
|
||||
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
||||
mid_pos = vector(pin_pos.x, rail_pos.y)
|
||||
self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos])
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
self.add_path(self.input_layer, [rail_pos, pin_pos])
|
||||
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer=self.decoder_layers[row_remainder][0],
|
||||
offset=rail_pos)
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.input_layer,
|
||||
offset=rail_pos,
|
||||
directions=self.bus_directions)
|
||||
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer=self.decoder_layers[row_remainder][2],
|
||||
to_layer=self.input_layer,
|
||||
offset=pin_pos,
|
||||
directions=("H", "H"))
|
||||
|
||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset, y_offset):
|
||||
"""
|
||||
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.
|
||||
# It would be fixed with a channel router.
|
||||
# pin_pos = pin.center()
|
||||
# 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_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||
pin_pos = pin.rc()
|
||||
mid_point1 = vector(x_offset, pin_pos.y)
|
||||
mid_point2 = vector(x_offset, y_offset)
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||
self.add_path(self.output_layer, [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||
|
||||
pin_pos = pin.center()
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
self.add_path("m1", [pin_pos, rail_pos])
|
||||
# pin_pos = pin.center()
|
||||
# rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
# self.add_path(self.output_layer, [pin_pos, rail_pos])
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m1",
|
||||
to_layer=self.output_layer,
|
||||
offset=pin_pos)
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=rail_pos)
|
||||
self.add_via_stack_center(from_layer=self.bus_layer,
|
||||
to_layer=self.output_layer,
|
||||
offset=rail_pos,
|
||||
directions=self.bus_directions)
|
||||
|
||||
def input_load(self):
|
||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
||||
|
||||
Reference in New Issue
Block a user