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
|
2017-12-12 23:53:19 +01:00
|
|
|
import contact
|
2016-11-08 18:57:35 +01:00
|
|
|
from vector import vector
|
2019-01-17 01:15:38 +01:00
|
|
|
from sram_factory import factory
|
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_predecode(design.design):
|
|
|
|
|
"""
|
|
|
|
|
Pre 2x4 and 3x8 decoder shared code.
|
|
|
|
|
"""
|
2019-01-17 01:15:38 +01:00
|
|
|
def __init__(self, name, input_number, height=None):
|
2017-08-24 00:02:15 +02:00
|
|
|
self.number_of_inputs = input_number
|
2018-11-29 01:59:58 +01:00
|
|
|
self.cell_height = height
|
2017-08-24 00:02:15 +02:00
|
|
|
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
2019-01-17 01:15:38 +01:00
|
|
|
design.design.__init__(self, name)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def add_pins(self):
|
|
|
|
|
for k in range(self.number_of_inputs):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin("in_{0}".format(k), "INPUT")
|
2016-11-08 18:57:35 +01:00
|
|
|
for i in range(self.number_of_outputs):
|
2019-08-06 23:14:09 +02:00
|
|
|
self.add_pin("out_{0}".format(i), "OUTPUT")
|
|
|
|
|
self.add_pin("vdd", "POWER")
|
|
|
|
|
self.add_pin("gnd", "GROUND")
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-11-14 01:05:22 +01:00
|
|
|
def add_modules(self):
|
2020-03-06 22:26:40 +01:00
|
|
|
""" Add the INV and AND gate modules """
|
2017-08-24 00:02:15 +02:00
|
|
|
|
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)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
self.add_and(self.number_of_inputs)
|
|
|
|
|
self.add_mod(self.and_mod)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
def add_and(self, inputs):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Create the NAND for the predecode input stage """
|
|
|
|
|
if inputs==2:
|
2020-03-06 22:26:40 +01:00
|
|
|
self.and_mod = factory.create(module_type="pand2",
|
|
|
|
|
height=self.cell_height)
|
2017-08-24 00:02:15 +02:00
|
|
|
elif inputs==3:
|
2020-03-06 22:26:40 +01:00
|
|
|
self.and_mod = factory.create(module_type="pand3",
|
|
|
|
|
height=self.cell_height)
|
2017-08-24 00:02:15 +02:00
|
|
|
else:
|
2020-02-06 17:20:09 +01:00
|
|
|
debug.error("Invalid number of predecode inputs: {}".format(inputs), -1)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-08-28 01:42:48 +02:00
|
|
|
def setup_layout_constraints(self):
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
self.height = self.number_of_outputs * self.and_mod.height
|
2018-07-17 23:23:06 +02:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# x offset for input inverters
|
2017-12-12 23:53:19 +01:00
|
|
|
self.x_off_inv_1 = self.number_of_inputs*self.m2_pitch
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
# 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
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
# x offset to output inverters
|
2020-03-06 22:26:40 +01:00
|
|
|
self.width = self.x_off_and + self.and_mod.width
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def route_rails(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
2018-10-11 18:53:08 +02:00
|
|
|
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
2020-03-06 22:26:40 +01:00
|
|
|
offset = vector(0.5 * self.m2_width, self.m1_pitch)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
2018-07-17 23:23:06 +02:00
|
|
|
pitch=self.m2_pitch,
|
|
|
|
|
offset=offset,
|
|
|
|
|
names=input_names,
|
2020-03-06 22:26:40 +01:00
|
|
|
length=self.height - 2 * self.m1_pitch)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2018-10-11 18:53:08 +02:00
|
|
|
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)]
|
2018-07-17 23:23:06 +02:00
|
|
|
decode_names = invert_names + non_invert_names
|
2020-03-06 22:26:40 +01:00
|
|
|
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m1_pitch)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.decode_rails = self.create_vertical_bus(layer="m2",
|
2018-07-17 23:23:06 +02:00
|
|
|
pitch=self.m2_pitch,
|
|
|
|
|
offset=offset,
|
|
|
|
|
names=decode_names,
|
2020-03-06 22:26:40 +01:00
|
|
|
length=self.height - 2 * self.m1_pitch)
|
2018-07-17 23:23:06 +02:00
|
|
|
|
2018-08-27 19:42:40 +02:00
|
|
|
def create_input_inverters(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
""" Create the input inverters to invert input signals for the decode stage. """
|
2017-12-12 23:53:19 +01:00
|
|
|
self.in_inst = []
|
2018-08-27 19:42:40 +02:00
|
|
|
for inv_num in range(self.number_of_inputs):
|
2018-11-27 21:02:53 +01:00
|
|
|
name = "pre_inv_{0}".format(inv_num)
|
2018-08-27 19:42:40 +02:00
|
|
|
self.in_inst.append(self.add_inst(name=name,
|
|
|
|
|
mod=self.inv))
|
2018-10-11 18:53:08 +02:00
|
|
|
self.connect_inst(["in_{0}".format(inv_num),
|
|
|
|
|
"inbar_{0}".format(inv_num),
|
2018-08-27 19:42:40 +02:00
|
|
|
"vdd", "gnd"])
|
|
|
|
|
|
|
|
|
|
def place_input_inverters(self):
|
|
|
|
|
""" Place the input inverters to invert input signals for the decode stage. """
|
2017-08-24 00:02:15 +02:00
|
|
|
for inv_num in range(self.number_of_inputs):
|
|
|
|
|
if (inv_num % 2 == 0):
|
|
|
|
|
y_off = inv_num * (self.inv.height)
|
|
|
|
|
mirror = "R0"
|
|
|
|
|
else:
|
|
|
|
|
y_off = (inv_num + 1) * (self.inv.height)
|
|
|
|
|
mirror="MX"
|
2017-09-30 01:22:13 +02:00
|
|
|
offset = vector(self.x_off_inv_1, y_off)
|
2018-08-28 02:25:39 +02:00
|
|
|
self.in_inst[inv_num].place(offset=offset,
|
|
|
|
|
mirror=mirror)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
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_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
|
2016-11-08 18:57:35 +01:00
|
|
|
mirror = "R0"
|
|
|
|
|
else:
|
2020-03-06 22:26:40 +01:00
|
|
|
y_off = (and_input + 1) * self.and_mod.height
|
2016-11-08 18:57:35 +01:00
|
|
|
mirror = "MX"
|
2020-03-06 22:26:40 +01:00
|
|
|
offset = vector(self.x_off_and, y_off)
|
|
|
|
|
self.and_inst[and_input].place(offset=offset,
|
|
|
|
|
mirror=mirror)
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def route(self):
|
|
|
|
|
self.route_input_inverters()
|
2017-08-24 00:02:15 +02:00
|
|
|
self.route_inputs_to_rails()
|
2020-03-06 22:26:40 +01:00
|
|
|
self.route_and_to_rails()
|
|
|
|
|
self.route_output_and()
|
2017-08-24 00:02:15 +02:00
|
|
|
self.route_vdd_gnd()
|
2016-11-08 18:57:35 +01:00
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def route_inputs_to_rails(self):
|
|
|
|
|
""" Route the uninverted inputs to the second set of rails """
|
|
|
|
|
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
|
2020-03-06 22:26:40 +01:00
|
|
|
# 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)
|
2018-10-11 18:53:08 +02:00
|
|
|
a_pin = "A_{}".format(num)
|
2020-03-06 22:26:40 +01:00
|
|
|
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,
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=[self.input_rails[in_pin].x, y_offset])
|
2020-03-06 22:26:40 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=[self.decode_rails[a_pin].x, y_offset])
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
def route_output_and(self):
|
2017-12-12 23:53:19 +01:00
|
|
|
"""
|
2020-03-06 22:26:40 +01:00
|
|
|
Route all conections of the outputs and gates
|
2017-12-12 23:53:19 +01:00
|
|
|
"""
|
|
|
|
|
for num in range(self.number_of_outputs):
|
|
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
z_pin = self.and_inst[num].get_pin("Z")
|
2018-10-11 18:53:08 +02:00
|
|
|
self.add_layout_pin(text="out_{}".format(num),
|
2019-12-17 20:03:36 +01:00
|
|
|
layer="m1",
|
2018-03-17 01:46:29 +01:00
|
|
|
offset=z_pin.ll(),
|
|
|
|
|
height=z_pin.height(),
|
|
|
|
|
width=z_pin.width())
|
2017-12-12 23:53:19 +01:00
|
|
|
|
2016-11-22 21:23:55 +01:00
|
|
|
def route_input_inverters(self):
|
2017-08-24 00:02:15 +02:00
|
|
|
"""
|
|
|
|
|
Route all conections of the inputs inverters [Inputs, outputs, vdd, gnd]
|
|
|
|
|
"""
|
|
|
|
|
for inv_num in range(self.number_of_inputs):
|
2018-10-11 18:53:08 +02:00
|
|
|
out_pin = "Abar_{}".format(inv_num)
|
|
|
|
|
in_pin = "in_{}".format(inv_num)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
#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
|
2020-03-06 22:26:40 +01:00
|
|
|
# pins in the and gates.
|
2017-12-12 23:53:19 +01:00
|
|
|
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)
|
2018-07-17 23:23:06 +02:00
|
|
|
rail_pos = vector(self.decode_rails[out_pin].x,y_offset)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_path("m1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers = self.m1_stack,
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=rail_pos)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
|
2016-11-22 21:23:55 +01:00
|
|
|
#route input
|
2017-12-12 23:53:19 +01:00
|
|
|
inv_in_pos = self.in_inst[inv_num].get_pin("A").lc()
|
2018-07-17 23:23:06 +02:00
|
|
|
in_pos = vector(self.input_rails[in_pin].x,inv_in_pos.y)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_path("m1", [in_pos, inv_in_pos])
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=in_pos)
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2020-03-06 22:26:40 +01:00
|
|
|
def route_and_to_rails(self):
|
|
|
|
|
# This 2D array defines the connection mapping
|
|
|
|
|
and_input_line_combination = self.get_and_input_line_combination()
|
2016-11-22 21:23:55 +01:00
|
|
|
for k in range(self.number_of_outputs):
|
2020-03-06 22:26:40 +01:00
|
|
|
# create x offset list
|
|
|
|
|
index_lst= and_input_line_combination[k]
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
if self.number_of_inputs == 2:
|
2018-02-02 23:08:56 +01:00
|
|
|
gate_lst = ["A","B"]
|
2017-08-24 00:02:15 +02:00
|
|
|
else:
|
2018-02-02 23:08:56 +01:00
|
|
|
gate_lst = ["A","B","C"]
|
2017-08-24 00:02:15 +02:00
|
|
|
|
|
|
|
|
# this will connect pins A,B or A,B,C
|
|
|
|
|
for rail_pin,gate_pin in zip(index_lst,gate_lst):
|
2020-03-06 22:26:40 +01:00
|
|
|
pin_pos = self.and_inst[k].get_pin(gate_pin).lc()
|
2018-07-17 23:23:06 +02:00
|
|
|
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_path("m1", [rail_pos, pin_pos])
|
2019-12-13 23:13:41 +01:00
|
|
|
self.add_via_center(layers=self.m1_stack,
|
2019-04-01 23:23:47 +02:00
|
|
|
offset=rail_pos)
|
|
|
|
|
|
2017-08-24 00:02:15 +02:00
|
|
|
def route_vdd_gnd(self):
|
|
|
|
|
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
|
|
|
|
|
2018-04-11 20:10:34 +02:00
|
|
|
# Find the x offsets for where the vias/pins should be placed
|
2020-02-06 17:20:09 +01:00
|
|
|
in_xoffset = self.in_inst[0].rx() + self.m1_space
|
2020-03-06 22:26:40 +01:00
|
|
|
# out_xoffset = self.and_inst[0].cx() + self.m1_space
|
|
|
|
|
for num in range(0, self.number_of_outputs):
|
2017-08-24 00:02:15 +02:00
|
|
|
# this will result in duplicate polygons for rails, but who cares
|
|
|
|
|
|
2018-04-11 20:10:34 +02:00
|
|
|
# Route both supplies
|
|
|
|
|
for n in ["vdd", "gnd"]:
|
2020-03-06 22:26:40 +01:00
|
|
|
and_pin = self.and_inst[num].get_pin(n)
|
|
|
|
|
supply_offset = and_pin.ll().scale(0, 1)
|
2019-12-17 20:03:36 +01:00
|
|
|
self.add_rect(layer="m1",
|
2018-04-11 20:10:34 +02:00
|
|
|
offset=supply_offset,
|
2020-03-06 22:26:40 +01:00
|
|
|
width=self.and_inst[num].rx())
|
2017-08-24 00:02:15 +02:00
|
|
|
|
2018-04-11 20:10:34 +02:00
|
|
|
# Add pins in two locations
|
2020-03-06 22:26:40 +01:00
|
|
|
for xoffset in [in_xoffset]:
|
|
|
|
|
pin_pos = vector(xoffset, and_pin.cy())
|
2018-10-11 00:15:58 +02:00
|
|
|
self.add_power_pin(n, pin_pos)
|
2018-04-11 20:10:34 +02:00
|
|
|
|
2016-11-22 21:23:55 +01:00
|
|
|
|
|
|
|
|
|