2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 Regents of the University of California and The Board
|
|
|
|
|
# of Regents for the Oklahoma Agricultural and Mechanical College
|
|
|
|
|
# (acting for and on behalf of Oklahoma State University)
|
|
|
|
|
# All rights reserved.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
|
|
|
|
import design
|
|
|
|
|
import math
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
2016-11-08 18:57:35 +01:00
|
|
|
from vector import vector
|
|
|
|
|
from globals import OPTS
|
2020-04-09 01:45:28 +02:00
|
|
|
from errors import drc_error
|
2020-04-09 20:38:18 +02:00
|
|
|
from tech import cell_properties
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-02-06 17:20:09 +01:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
class hierarchical_decoder(design.design):
|
|
|
|
|
"""
|
|
|
|
|
Dynamically generated hierarchical decoder.
|
|
|
|
|
"""
|
2020-04-09 02:05:16 +02:00
|
|
|
def __init__(self, name, num_outputs):
|
2019-01-17 01:15:38 +01:00
|
|
|
design.design.__init__(self, name)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
self.AND_FORMAT = "DEC_AND_{0}"
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
self.pre2x4_inst = []
|
|
|
|
|
self.pre3x8_inst = []
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-09 20:38:18 +02:00
|
|
|
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
|
|
|
|
|
|
2020-04-09 02:05:16 +02:00
|
|
|
self.num_outputs = num_outputs
|
|
|
|
|
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
|
2020-02-06 17:20:09 +01:00
|
|
|
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
2018-03-12 21:14:53 +01:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
self.create_netlist()
|
2018-08-28 01:42:48 +02:00
|
|
|
if not OPTS.netlist_only:
|
|
|
|
|
self.create_layout()
|
2018-03-17 01:46:29 +01:00
|
|
|
|
2020-04-02 00:54:06 +02:00
|
|
|
def find_decoder_height(self):
|
2020-04-09 20:38:18 +02:00
|
|
|
"""
|
|
|
|
|
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.
|
|
|
|
|
"""
|
2020-04-02 00:54:06 +02:00
|
|
|
b = factory.create(module_type="bitcell")
|
2020-04-10 22:29:41 +02:00
|
|
|
|
|
|
|
|
# Old behavior
|
|
|
|
|
if OPTS.netlist_only:
|
|
|
|
|
return (b.height, 1)
|
2020-04-02 21:52:42 +02:00
|
|
|
|
|
|
|
|
# Search for the smallest multiple that works
|
2020-04-02 18:47:39 +02:00
|
|
|
cell_multiple = 1
|
2020-04-09 01:45:28 +02:00
|
|
|
while cell_multiple < 5:
|
2020-04-02 18:47:39 +02:00
|
|
|
cell_height = cell_multiple * b.height
|
2020-04-09 01:45:28 +02:00
|
|
|
# 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)
|
|
|
|
|
|
2020-04-02 18:47:39 +02:00
|
|
|
cell_multiple += 1
|
2020-04-09 01:45:28 +02:00
|
|
|
|
2020-04-02 18:47:39 +02:00
|
|
|
else:
|
|
|
|
|
debug.error("Couldn't find a valid decoder height multiple.", -1)
|
2020-04-02 00:54:06 +02:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def create_netlist(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_modules()
|
2018-08-27 19:42:40 +02:00
|
|
|
self.setup_netlist_constants()
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_pins()
|
|
|
|
|
self.create_pre_decoder()
|
|
|
|
|
self.create_row_decoder()
|
|
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def create_layout(self):
|
|
|
|
|
self.setup_layout_constants()
|
|
|
|
|
self.place_pre_decoder()
|
|
|
|
|
self.place_row_decoder()
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_inputs()
|
|
|
|
|
self.route_decoder_bus()
|
2018-08-27 19:42:40 +02:00
|
|
|
self.route_vdd_gnd()
|
2018-08-27 23:18:32 +02:00
|
|
|
self.offset_all_coordinates()
|
2019-05-28 01:32:38 +02:00
|
|
|
self.add_boundary()
|
2018-08-27 23:18:32 +02:00
|
|
|
self.DRC_LVS()
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def add_modules(self):
|
2019-01-17 01:15:38 +01:00
|
|
|
self.inv = factory.create(module_type="pinv",
|
|
|
|
|
height=self.cell_height)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.inv)
|
2020-03-06 22:26:40 +01:00
|
|
|
self.and2 = factory.create(module_type="pand2",
|
|
|
|
|
height=self.cell_height)
|
|
|
|
|
self.add_mod(self.and2)
|
|
|
|
|
self.and3 = factory.create(module_type="pand3",
|
2019-01-17 01:15:38 +01:00
|
|
|
height=self.cell_height)
|
2020-03-06 22:26:40 +01:00
|
|
|
self.add_mod(self.and3)
|
2018-03-12 21:14:53 +01:00
|
|
|
|
|
|
|
|
self.add_decoders()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-03-12 21:14:53 +01:00
|
|
|
def add_decoders(self):
|
|
|
|
|
""" Create the decoders based on the number of pre-decodes """
|
2019-01-17 01:15:38 +01:00
|
|
|
self.pre2_4 = factory.create(module_type="hierarchical_predecode2x4",
|
|
|
|
|
height=self.cell_height)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.pre2_4)
|
2018-03-12 21:14:53 +01:00
|
|
|
|
2019-01-17 01:15:38 +01:00
|
|
|
self.pre3_8 = factory.create(module_type="hierarchical_predecode3x8",
|
|
|
|
|
height=self.cell_height)
|
2016-11-08 18:57:35 +01:00
|
|
|
self.add_mod(self.pre3_8)
|
|
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
def determine_predecodes(self, num_inputs):
|
2018-07-09 18:57:57 +02:00
|
|
|
""" Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
|
|
|
|
|
needed based on the number of inputs """
|
2016-11-08 18:57:35 +01:00
|
|
|
if (num_inputs == 2):
|
2020-02-28 19:24:39 +01:00
|
|
|
return (1, 0)
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (num_inputs == 3):
|
2020-02-28 19:24:39 +01:00
|
|
|
return(0, 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (num_inputs == 4):
|
2020-02-28 19:24:39 +01:00
|
|
|
return(2, 0)
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (num_inputs == 5):
|
2020-02-28 19:24:39 +01:00
|
|
|
return(1, 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (num_inputs == 6):
|
2020-02-28 19:24:39 +01:00
|
|
|
return(3, 0)
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (num_inputs == 7):
|
2020-02-28 19:24:39 +01:00
|
|
|
return(2, 1)
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (num_inputs == 8):
|
2020-02-28 19:24:39 +01:00
|
|
|
return(1, 2)
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (num_inputs == 9):
|
2020-02-28 19:24:39 +01:00
|
|
|
return(0, 3)
|
2016-11-08 18:57:35 +01:00
|
|
|
else:
|
2020-02-28 19:24:39 +01:00
|
|
|
debug.error("Invalid number of inputs for hierarchical decoder", -1)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def setup_netlist_constants(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
self.predec_groups = [] # This array is a 2D array.
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
# Distributing vertical bus to different groups. One group belongs to one pre-decoder.
|
2016-11-08 18:57:35 +01:00
|
|
|
# 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
|
|
|
|
|
# be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
|
2017-08-24 00:02:15 +02:00
|
|
|
# in self.predec_groups
|
2016-11-08 18:57:35 +01:00
|
|
|
index = 0
|
|
|
|
|
for i in range(self.no_of_pre2x4):
|
|
|
|
|
lines = []
|
|
|
|
|
for j in range(4):
|
|
|
|
|
lines.append(index)
|
|
|
|
|
index = index + 1
|
2017-08-24 00:02:15 +02:00
|
|
|
self.predec_groups.append(lines)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
for i in range(self.no_of_pre3x8):
|
|
|
|
|
lines = []
|
|
|
|
|
for j in range(8):
|
|
|
|
|
lines.append(index)
|
|
|
|
|
index = index + 1
|
2017-08-24 00:02:15 +02:00
|
|
|
self.predec_groups.append(lines)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def setup_layout_constants(self):
|
|
|
|
|
""" Calculate the overall dimensions of the hierarchical decoder """
|
|
|
|
|
|
|
|
|
|
# If we have 4 or fewer rows, the predecoder is the decoder itself
|
|
|
|
|
if self.num_inputs>=4:
|
2020-02-28 19:24:39 +01:00
|
|
|
self.total_number_of_predecoder_outputs = 4 * self.no_of_pre2x4 + 8 * self.no_of_pre3x8
|
2018-08-27 19:42:40 +02:00
|
|
|
else:
|
2020-02-28 19:24:39 +01:00
|
|
|
self.total_number_of_predecoder_outputs = 0
|
|
|
|
|
debug.error("Not enough rows ({}) for a hierarchical decoder. Non-hierarchical not supported yet.".format(self.num_inputs),
|
|
|
|
|
-1)
|
2018-08-27 19:42:40 +02:00
|
|
|
|
|
|
|
|
# Calculates height and width of pre-decoder,
|
|
|
|
|
if self.no_of_pre3x8 > 0:
|
2020-02-28 19:24:39 +01:00
|
|
|
self.predecoder_width = self.pre3_8.width
|
2018-08-27 19:42:40 +02:00
|
|
|
else:
|
|
|
|
|
self.predecoder_width = self.pre2_4.width
|
|
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
# 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)
|
2020-04-23 23:43:54 +02:00
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
# Calculates height and width of row-decoder
|
2018-08-27 19:42:40 +02:00
|
|
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
2020-03-06 22:26:40 +01:00
|
|
|
nand_width = self.and2.width
|
2020-04-23 23:43:54 +02:00
|
|
|
nand_inputs = 2
|
2018-08-27 19:42:40 +02:00
|
|
|
else:
|
2020-03-06 22:26:40 +01:00
|
|
|
nand_width = self.and3.width
|
2020-04-23 23:43:54 +02:00
|
|
|
nand_inputs = 3
|
|
|
|
|
self.internal_routing_width = self.m3_pitch * (self.total_number_of_predecoder_outputs + 1)
|
2020-04-10 22:29:41 +02:00
|
|
|
self.row_decoder_height = self.inv.height * self.num_rows
|
2020-04-23 23:43:54 +02:00
|
|
|
|
|
|
|
|
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m3_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.m3_pitch
|
2020-02-28 19:24:39 +01:00
|
|
|
# Calculates height and width of hierarchical decoder
|
2020-04-14 19:52:25 +02:00
|
|
|
# Add extra pitch for good measure
|
|
|
|
|
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch
|
2018-08-27 19:42:40 +02:00
|
|
|
self.width = self.input_routing_width + self.predecoder_width \
|
2020-04-10 22:29:41 +02:00
|
|
|
+ self.internal_routing_width \
|
|
|
|
|
+ self.decoders_per_row * nand_width + self.inv.width
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
def route_inputs(self):
|
|
|
|
|
""" Create input bus for the predecoders """
|
2018-07-09 22:16:38 +02:00
|
|
|
# inputs should be as high as the decoders
|
2020-02-28 19:24:39 +01:00
|
|
|
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
|
2018-07-09 22:16:38 +02:00
|
|
|
|
|
|
|
|
# Find the left-most predecoder
|
|
|
|
|
min_x = 0
|
|
|
|
|
if self.no_of_pre2x4 > 0:
|
2020-04-10 22:29:41 +02:00
|
|
|
min_x = min(min_x, self.pre2x4_inst[0].lx())
|
2018-07-09 22:16:38 +02:00
|
|
|
if self.no_of_pre3x8 > 0:
|
2020-04-10 22:29:41 +02:00
|
|
|
min_x = min(min_x, self.pre3x8_inst[0].lx())
|
2020-02-28 19:24:39 +01:00
|
|
|
input_offset=vector(min_x - self.input_routing_width, 0)
|
2018-07-10 19:06:59 +02:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
2020-04-23 23:43:54 +02:00
|
|
|
self.input_bus = self.create_vertical_pin_bus(layer="m3",
|
|
|
|
|
pitch=self.m3_pitch,
|
2020-04-10 22:29:41 +02:00
|
|
|
offset=input_offset,
|
|
|
|
|
names=input_bus_names,
|
|
|
|
|
length=input_height)
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
self.route_input_to_predecodes()
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def route_input_to_predecodes(self):
|
|
|
|
|
""" Route the vertical input rail to the predecoders """
|
2018-07-09 22:16:38 +02:00
|
|
|
for pre_num in range(self.no_of_pre2x4):
|
|
|
|
|
for i in range(2):
|
|
|
|
|
index = pre_num * 2 + i
|
|
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
input_pos = self.input_bus["addr_{}".format(index)]
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
in_name = "in_{}".format(i)
|
2018-07-09 22:16:38 +02:00
|
|
|
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
|
|
|
|
|
|
2018-07-18 19:21:58 +02:00
|
|
|
# To prevent conflicts, we will offset each input connect so
|
|
|
|
|
# that it aligns with the vdd/gnd rails
|
2020-04-23 23:43:54 +02:00
|
|
|
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
2020-02-28 19:24:39 +01:00
|
|
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_input_bus(decoder_offset, input_offset)
|
2018-07-09 22:16:38 +02:00
|
|
|
|
|
|
|
|
for pre_num in range(self.no_of_pre3x8):
|
|
|
|
|
for i in range(3):
|
|
|
|
|
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
|
|
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
input_pos = self.input_bus["addr_{}".format(index)]
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
in_name = "in_{}".format(i)
|
2018-07-09 22:16:38 +02:00
|
|
|
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
|
|
|
|
|
|
2018-07-18 19:21:58 +02:00
|
|
|
# To prevent conflicts, we will offset each input connect so
|
|
|
|
|
# that it aligns with the vdd/gnd rails
|
2020-04-23 23:43:54 +02:00
|
|
|
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
|
2020-02-28 19:24:39 +01:00
|
|
|
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_input_bus(decoder_offset, input_offset)
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
def route_input_bus(self, input_offset, output_offset):
|
2020-04-14 19:52:25 +02:00
|
|
|
"""
|
|
|
|
|
Route a vertical M2 coordinate to another
|
|
|
|
|
vertical M2 coordinate to the predecode inputs
|
|
|
|
|
"""
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-04-23 23:43:54 +02:00
|
|
|
self.add_via_stack_center(from_layer="m2",
|
|
|
|
|
to_layer="m3",
|
|
|
|
|
offset=input_offset,
|
|
|
|
|
directions="nonpref")
|
|
|
|
|
self.add_via_stack_center(from_layer="m2",
|
|
|
|
|
to_layer="m3",
|
|
|
|
|
offset=output_offset,
|
|
|
|
|
directions="nonpref")
|
|
|
|
|
self.add_path(("m2"), [input_offset, output_offset])
|
2018-07-09 22:16:38 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def add_pins(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Add the module pins """
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
for i in range(self.num_inputs):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin("addr_{0}".format(i), "INPUT")
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-09 02:05:16 +02:00
|
|
|
for j in range(self.num_outputs):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin("decode_{0}".format(j), "OUTPUT")
|
|
|
|
|
self.add_pin("vdd", "POWER")
|
|
|
|
|
self.add_pin("gnd", "GROUND")
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def create_pre_decoder(self):
|
|
|
|
|
""" Creates pre-decoder and places labels input address [A] """
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
for i in range(self.no_of_pre2x4):
|
2018-08-27 19:42:40 +02:00
|
|
|
self.create_pre2x4(i)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
for i in range(self.no_of_pre3x8):
|
2018-08-27 19:42:40 +02:00
|
|
|
self.create_pre3x8(i)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
def create_pre2x4(self, num):
|
2018-07-09 18:57:57 +02:00
|
|
|
""" Add a 2x4 predecoder to the left of the origin """
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
if (self.num_inputs == 2):
|
|
|
|
|
index_off1 = index_off2 = 0
|
|
|
|
|
else:
|
2017-08-24 00:02:15 +02:00
|
|
|
index_off1 = num * 2
|
|
|
|
|
index_off2 = num * 4
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
pins = []
|
|
|
|
|
for input_index in range(2):
|
2018-10-11 18:53:08 +02:00
|
|
|
pins.append("addr_{0}".format(input_index + index_off1))
|
2016-11-08 18:57:35 +01:00
|
|
|
for output_index in range(4):
|
2018-10-11 18:53:08 +02:00
|
|
|
pins.append("out_{0}".format(output_index + index_off2))
|
2017-08-24 00:02:15 +02:00
|
|
|
pins.extend(["vdd", "gnd"])
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
self.pre2x4_inst.append(self.add_inst(name="pre_{0}".format(num),
|
2018-08-27 19:42:40 +02:00
|
|
|
mod=self.pre2_4))
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_inst(pins)
|
|
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
def create_pre3x8(self, num):
|
2018-07-09 18:57:57 +02:00
|
|
|
""" Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
2017-08-24 00:02:15 +02:00
|
|
|
# If we had 2x4 predecodes, those are used as the lower
|
|
|
|
|
# decode output bits
|
|
|
|
|
in_index_offset = num * 3 + self.no_of_pre2x4 * 2
|
|
|
|
|
out_index_offset = num * 8 + self.no_of_pre2x4 * 4
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
pins = []
|
|
|
|
|
for input_index in range(3):
|
2018-10-11 18:53:08 +02:00
|
|
|
pins.append("addr_{0}".format(input_index + in_index_offset))
|
2016-11-08 18:57:35 +01:00
|
|
|
for output_index in range(8):
|
2018-10-11 18:53:08 +02:00
|
|
|
pins.append("out_{0}".format(output_index + out_index_offset))
|
2017-08-24 00:02:15 +02:00
|
|
|
pins.extend(["vdd", "gnd"])
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
self.pre3x8_inst.append(self.add_inst(name="pre3x8_{0}".format(num),
|
2018-08-27 19:42:40 +02:00
|
|
|
mod=self.pre3_8))
|
2016-11-08 18:57:35 +01:00
|
|
|
self.connect_inst(pins)
|
|
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def place_pre_decoder(self):
|
|
|
|
|
""" Creates pre-decoder and places labels input address [A] """
|
|
|
|
|
|
|
|
|
|
for i in range(self.no_of_pre2x4):
|
|
|
|
|
self.place_pre2x4(i)
|
|
|
|
|
|
|
|
|
|
for i in range(self.no_of_pre3x8):
|
|
|
|
|
self.place_pre3x8(i)
|
|
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
def place_pre2x4(self, num):
|
2018-08-27 19:42:40 +02:00
|
|
|
""" Place 2x4 predecoder to the left of the origin """
|
|
|
|
|
|
|
|
|
|
if (self.num_inputs == 2):
|
2020-02-28 19:24:39 +01:00
|
|
|
base = vector(-self.pre2_4.width, 0)
|
2018-08-27 19:42:40 +02:00
|
|
|
else:
|
|
|
|
|
base= vector(-self.pre2_4.width, num * self.pre2_4.height)
|
|
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-02-28 19:24:39 +01:00
|
|
|
def place_pre3x8(self, num):
|
2018-08-27 19:42:40 +02:00
|
|
|
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
|
|
|
|
if (self.num_inputs == 3):
|
2020-02-28 19:24:39 +01:00
|
|
|
offset = vector(-self.pre_3_8.width, 0)
|
2018-08-27 19:42:40 +02:00
|
|
|
else:
|
2020-02-28 19:24:39 +01:00
|
|
|
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
|
2018-08-27 19:42:40 +02:00
|
|
|
offset = vector(-self.pre3_8.width, height)
|
|
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def create_row_decoder(self):
|
2020-03-06 22:26:40 +01:00
|
|
|
""" Create the row-decoder by placing AND2/AND3 and Inverters
|
2017-08-24 00:02:15 +02:00
|
|
|
and add the primary decoder output pins. """
|
2016-11-08 18:57:35 +01:00
|
|
|
if (self.num_inputs >= 4):
|
2020-03-06 22:26:40 +01:00
|
|
|
self.create_decoder_and_array()
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
def create_decoder_and_array(self):
|
|
|
|
|
""" Add a column of AND gates for final decode """
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
self.and_inst = []
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
# Row Decoder AND GATE array for address inputs <5.
|
2016-11-08 18:57:35 +01:00
|
|
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
2017-08-24 00:02:15 +02:00
|
|
|
for i in range(len(self.predec_groups[0])):
|
|
|
|
|
for j in range(len(self.predec_groups[1])):
|
2020-04-10 22:29:41 +02:00
|
|
|
output = len(self.predec_groups[0]) * j + i
|
|
|
|
|
if (output < self.num_outputs):
|
|
|
|
|
name = self.AND_FORMAT.format(output)
|
2020-03-06 22:26:40 +01:00
|
|
|
self.and_inst.append(self.add_inst(name=name,
|
|
|
|
|
mod=self.and2))
|
2019-11-15 04:59:57 +01:00
|
|
|
pins =["out_{0}".format(i),
|
|
|
|
|
"out_{0}".format(j + len(self.predec_groups[0])),
|
2020-04-10 22:29:41 +02:00
|
|
|
"decode_{0}".format(output),
|
2019-11-15 04:59:57 +01:00
|
|
|
"vdd", "gnd"]
|
|
|
|
|
self.connect_inst(pins)
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
# Row Decoder AND GATE array for address inputs >5.
|
2016-11-08 18:57:35 +01:00
|
|
|
elif (self.num_inputs > 5):
|
2017-08-24 00:02:15 +02:00
|
|
|
for i in range(len(self.predec_groups[0])):
|
|
|
|
|
for j in range(len(self.predec_groups[1])):
|
|
|
|
|
for k in range(len(self.predec_groups[2])):
|
2020-04-10 22:29:41 +02:00
|
|
|
output = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
|
|
|
|
|
+ len(self.predec_groups[0]) * j + i
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
if (output < self.num_outputs):
|
|
|
|
|
name = self.AND_FORMAT.format(output)
|
2020-03-06 22:26:40 +01:00
|
|
|
self.and_inst.append(self.add_inst(name=name,
|
|
|
|
|
mod=self.and3))
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2019-11-15 04:59:57 +01:00
|
|
|
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])),
|
2020-04-10 22:29:41 +02:00
|
|
|
"decode_{0}".format(output),
|
2019-11-15 04:59:57 +01:00
|
|
|
"vdd", "gnd"]
|
|
|
|
|
self.connect_inst(pins)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def place_row_decoder(self):
|
2020-02-28 19:24:39 +01:00
|
|
|
"""
|
2020-03-06 22:26:40 +01:00
|
|
|
Place the row-decoder by placing AND2/AND3 and Inverters
|
2020-02-28 19:24:39 +01:00
|
|
|
and add the primary decoder output pins.
|
2018-08-27 19:42:40 +02:00
|
|
|
"""
|
|
|
|
|
if (self.num_inputs >= 4):
|
2020-03-06 22:26:40 +01:00
|
|
|
self.place_decoder_and_array()
|
2018-08-27 19:42:40 +02:00
|
|
|
self.route_decoder()
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
def place_decoder_and_array(self):
|
2020-04-10 22:29:41 +02:00
|
|
|
"""
|
|
|
|
|
Add a column of AND gates for final decode.
|
|
|
|
|
This may have more than one decoder per row to match the bitcell height.
|
|
|
|
|
"""
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
# Row Decoder AND GATE array for address inputs <5.
|
2018-08-27 19:42:40 +02:00
|
|
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
2020-03-06 22:26:40 +01:00
|
|
|
self.place_and_array(and_mod=self.and2)
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
# Row Decoder AND GATE array for address inputs >5.
|
2018-08-27 19:42:40 +02:00
|
|
|
# FIXME: why this correct offset?)
|
|
|
|
|
elif (self.num_inputs > 5):
|
2020-03-06 22:26:40 +01:00
|
|
|
self.place_and_array(and_mod=self.and3)
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
def place_and_array(self, and_mod):
|
2020-04-10 22:29:41 +02:00
|
|
|
"""
|
|
|
|
|
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
|
2018-08-27 19:42:40 +02:00
|
|
|
if ((row % 2) == 0):
|
2020-03-06 22:26:40 +01:00
|
|
|
y_off = and_mod.height * row
|
2018-08-27 19:42:40 +02:00
|
|
|
mirror = "R0"
|
|
|
|
|
else:
|
2020-03-06 22:26:40 +01:00
|
|
|
y_off = and_mod.height * (row + 1)
|
2018-08-27 19:42:40 +02:00
|
|
|
mirror = "MX"
|
|
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
x_off = self.internal_routing_width + dec * and_mod.width
|
|
|
|
|
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
|
|
|
|
mirror=mirror)
|
2018-08-27 19:42:40 +02:00
|
|
|
|
2017-12-12 23:53:19 +01:00
|
|
|
def route_decoder(self):
|
2020-03-06 22:26:40 +01:00
|
|
|
""" Add the pins. """
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
for output in range(self.num_outputs):
|
|
|
|
|
z_pin = self.and_inst[output].get_pin("Z")
|
|
|
|
|
self.add_layout_pin(text="decode_{0}".format(output),
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m1",
|
2017-12-12 23:53:19 +01:00
|
|
|
offset=z_pin.ll(),
|
2017-08-24 00:02:15 +02:00
|
|
|
width=z_pin.width(),
|
2017-12-12 23:53:19 +01:00
|
|
|
height=z_pin.height())
|
|
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
def route_decoder_bus(self):
|
|
|
|
|
"""
|
|
|
|
|
Creates vertical metal 2 bus to connect predecoder and decoder stages.
|
|
|
|
|
"""
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
# This is not needed for inputs <4 since they have no pre/decode stages.
|
2016-11-08 18:57:35 +01:00
|
|
|
if (self.num_inputs >= 4):
|
2020-04-10 22:29:41 +02:00
|
|
|
# This leaves an offset for the predecoder output jogs
|
2018-10-11 18:53:08 +02:00
|
|
|
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
2020-04-23 23:43:54 +02:00
|
|
|
self.predecode_bus = self.create_vertical_pin_bus(layer="m3",
|
|
|
|
|
pitch=self.m3_pitch,
|
2020-04-10 22:29:41 +02:00
|
|
|
offset=vector(0, 0),
|
|
|
|
|
names=input_bus_names,
|
|
|
|
|
length=self.height)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-14 19:52:25 +02:00
|
|
|
self.route_predecodes_to_bus()
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_bus_to_decoder()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-04-14 19:52:25 +02:00
|
|
|
def route_predecodes_to_bus(self):
|
2020-04-10 22:29:41 +02:00
|
|
|
"""
|
|
|
|
|
Iterates through all of the predecodes
|
|
|
|
|
and connects to the rails including the offsets
|
|
|
|
|
"""
|
2018-07-18 19:21:58 +02:00
|
|
|
# FIXME: convert to connect_bus
|
2017-12-12 23:53:19 +01:00
|
|
|
for pre_num in range(self.no_of_pre2x4):
|
|
|
|
|
for i in range(4):
|
2018-10-11 18:53:08 +02:00
|
|
|
predecode_name = "predecode_{}".format(pre_num * 4 + i)
|
|
|
|
|
out_name = "out_{}".format(i)
|
2017-12-12 23:53:19 +01:00
|
|
|
pin = self.pre2x4_inst[pre_num].get_pin(out_name)
|
2020-04-10 22:29:41 +02:00
|
|
|
x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
|
|
|
|
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-07-18 19:21:58 +02:00
|
|
|
# FIXME: convert to connect_bus
|
2017-12-12 23:53:19 +01:00
|
|
|
for pre_num in range(self.no_of_pre3x8):
|
|
|
|
|
for i in range(8):
|
2018-10-11 18:53:08 +02:00
|
|
|
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
|
|
|
|
|
out_name = "out_{}".format(i)
|
2017-12-12 23:53:19 +01:00
|
|
|
pin = self.pre3x8_inst[pre_num].get_pin(out_name)
|
2020-04-10 22:29:41 +02:00
|
|
|
x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
|
|
|
|
|
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
def route_bus_to_decoder(self):
|
|
|
|
|
"""
|
|
|
|
|
Use the self.predec_groups to determine the connections to the decoder AND gates.
|
2020-03-06 22:26:40 +01:00
|
|
|
Inputs of AND2/AND3 gates come from different groups.
|
2020-04-10 22:29:41 +02:00
|
|
|
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]
|
2017-08-24 00:02:15 +02:00
|
|
|
"""
|
2020-04-10 22:29:41 +02:00
|
|
|
output_index = 0
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
2018-11-28 23:09:45 +01:00
|
|
|
for index_B in self.predec_groups[1]:
|
|
|
|
|
for index_A in self.predec_groups[0]:
|
2018-07-18 19:21:58 +02:00
|
|
|
# FIXME: convert to connect_bus?
|
2020-04-10 22:29:41 +02:00
|
|
|
if (output_index < self.num_outputs):
|
|
|
|
|
row_index = math.floor(output_index / self.decoders_per_row)
|
|
|
|
|
row_remainder = (output_index % self.decoders_per_row)
|
2020-04-14 19:52:25 +02:00
|
|
|
row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch
|
2019-11-15 04:59:57 +01:00
|
|
|
predecode_name = "predecode_{}".format(index_A)
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_predecode_bus_outputs(predecode_name,
|
|
|
|
|
self.and_inst[output_index].get_pin("A"),
|
|
|
|
|
row_offset)
|
2020-03-06 22:26:40 +01:00
|
|
|
predecode_name = "predecode_{}".format(index_B)
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_predecode_bus_outputs(predecode_name,
|
|
|
|
|
self.and_inst[output_index].get_pin("B"),
|
2020-04-14 19:52:25 +02:00
|
|
|
row_offset + self.m3_pitch)
|
2020-04-10 22:29:41 +02:00
|
|
|
output_index = output_index + 1
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
elif (self.num_inputs > 5):
|
2018-11-28 23:09:45 +01:00
|
|
|
for index_C in self.predec_groups[2]:
|
2017-08-24 00:02:15 +02:00
|
|
|
for index_B in self.predec_groups[1]:
|
2018-11-28 23:09:45 +01:00
|
|
|
for index_A in self.predec_groups[0]:
|
2018-07-18 19:21:58 +02:00
|
|
|
# FIXME: convert to connect_bus?
|
2020-04-10 22:29:41 +02:00
|
|
|
if (output_index < self.num_outputs):
|
|
|
|
|
row_index = math.floor(output_index / self.decoders_per_row)
|
|
|
|
|
row_remainder = (output_index % self.decoders_per_row)
|
2020-04-23 23:43:54 +02:00
|
|
|
row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m2_pitch
|
2020-02-28 19:24:39 +01:00
|
|
|
predecode_name = "predecode_{}".format(index_A)
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_predecode_bus_outputs(predecode_name,
|
|
|
|
|
self.and_inst[output_index].get_pin("A"),
|
|
|
|
|
row_offset)
|
2020-02-28 19:24:39 +01:00
|
|
|
predecode_name = "predecode_{}".format(index_B)
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_predecode_bus_outputs(predecode_name,
|
|
|
|
|
self.and_inst[output_index].get_pin("B"),
|
2020-04-23 23:43:54 +02:00
|
|
|
row_offset + self.m2_pitch)
|
2020-02-28 19:24:39 +01:00
|
|
|
predecode_name = "predecode_{}".format(index_C)
|
2020-04-10 22:29:41 +02:00
|
|
|
self.route_predecode_bus_outputs(predecode_name,
|
|
|
|
|
self.and_inst[output_index].get_pin("C"),
|
2020-04-23 23:43:54 +02:00
|
|
|
row_offset + 2 * self.m2_pitch)
|
2020-04-10 22:29:41 +02:00
|
|
|
output_index = output_index + 1
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def route_vdd_gnd(self):
|
2020-04-10 22:29:41 +02:00
|
|
|
"""
|
|
|
|
|
Add a pin for each row of vdd/gnd which are
|
|
|
|
|
must-connects next level up.
|
|
|
|
|
"""
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-04-14 19:52:25 +02:00
|
|
|
# The vias will be placed at the right of the cells.
|
|
|
|
|
xoffset = max(x.rx() for x in self.and_inst)
|
2020-04-09 02:05:16 +02:00
|
|
|
for num in range(0, self.num_outputs):
|
2020-04-14 19:52:25 +02:00
|
|
|
# Only add the power pin for the 1st in each row
|
|
|
|
|
if num % self.decoders_per_row:
|
|
|
|
|
continue
|
|
|
|
|
|
2018-10-20 21:54:12 +02:00
|
|
|
for pin_name in ["vdd", "gnd"]:
|
|
|
|
|
# The nand and inv are the same height rows...
|
2020-03-06 22:26:40 +01:00
|
|
|
supply_pin = self.and_inst[num].get_pin(pin_name)
|
2018-10-20 21:54:12 +02:00
|
|
|
pin_pos = vector(xoffset, supply_pin.cy())
|
2020-04-14 19:52:25 +02:00
|
|
|
self.add_path("m1",
|
|
|
|
|
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
|
2018-10-20 21:54:12 +02:00
|
|
|
self.add_power_pin(name=pin_name,
|
|
|
|
|
loc=pin_pos)
|
|
|
|
|
|
2018-04-11 20:56:41 +02:00
|
|
|
# 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")
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-04-10 22:29:41 +02:00
|
|
|
def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
|
|
|
|
|
"""
|
|
|
|
|
Connect the routing rail to the given metal1 pin
|
|
|
|
|
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)
|
2020-04-23 23:43:54 +02:00
|
|
|
self.add_path("m2", [rail_pos, pin_pos])
|
|
|
|
|
self.add_via_stack_center(from_layer=pin.layer,
|
|
|
|
|
to_layer="m2",
|
|
|
|
|
offset=pin_pos,
|
|
|
|
|
directions=("H", "H"))
|
|
|
|
|
self.add_via_stack_center(from_layer="m2",
|
|
|
|
|
to_layer="m3",
|
|
|
|
|
offset=rail_pos,
|
|
|
|
|
directions="nonpref")
|
2020-04-10 22:29:41 +02:00
|
|
|
# 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)
|
2020-04-23 23:43:54 +02:00
|
|
|
self.add_wire(self.m2_stack, [rail_pos, mid_pos, pin_pos])
|
2020-04-10 22:29:41 +02:00
|
|
|
self.add_via_center(layers=self.m2_stack,
|
|
|
|
|
offset=rail_pos)
|
2020-04-23 23:43:54 +02:00
|
|
|
self.add_via_stack_center(from_layer=pin_pos.layer,
|
|
|
|
|
to_layer="m3",
|
|
|
|
|
offset=pin_pos,
|
|
|
|
|
directions="nonpref")
|
2020-04-10 22:29:41 +02:00
|
|
|
|
|
|
|
|
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
|
|
|
|
"""
|
|
|
|
|
Connect the routing rail to the given metal1 pin using a jog
|
|
|
|
|
to the right of the cell at the given x_offset.
|
|
|
|
|
"""
|
2018-07-18 19:21:58 +02:00
|
|
|
# This routes the pin up to the rail, basically, to avoid conflicts.
|
|
|
|
|
# It would be fixed with a channel router.
|
2020-04-10 22:29:41 +02:00
|
|
|
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)
|
2020-04-23 23:43:54 +02:00
|
|
|
self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
|
|
|
|
|
self.add_via_stack_center(from_layer="m1",
|
|
|
|
|
to_layer="m3",
|
|
|
|
|
offset=rail_pos,
|
|
|
|
|
directions="nonpref")
|
2017-05-30 21:50:07 +02:00
|
|
|
|
|
|
|
|
def input_load(self):
|
|
|
|
|
if self.determine_predecodes(self.num_inputs)[1]==0:
|
|
|
|
|
pre = self.pre2_4
|
|
|
|
|
else:
|
|
|
|
|
pre = self.pre3_8
|
|
|
|
|
return pre.input_load()
|
2019-04-24 23:23:22 +02:00
|
|
|
|