Merge master branch into router

This commit is contained in:
Matt Guthaus
2017-01-09 14:04:37 -08:00
64 changed files with 459 additions and 15956 deletions
+8 -3
View File
@@ -384,12 +384,17 @@ class delay():
if (min_period0 == None) or (delay0 == None):
return None
debug.info(1, "Min Period for high_to_low transistion: {0}n with a delay of {1}".format(min_period0, delay0))
data = {"min_period1": min_period1, # period in ns
read_power=ch.convert_to_float(ch.parse_output("timing", "power_read"))
write_power=ch.convert_to_float(ch.parse_output("timing", "power_write"))
data = {"min_period1": min_period1, # period in ns
"delay1": delay1, # delay in s
"min_period0": min_period0,
"delay0": delay0
"delay0": delay0,
"read_power": read_power,
"write_power": write_power
}
return data
return data
def obtain_cycle_times(self, slow_period, fast_period):
+38 -10
View File
@@ -63,6 +63,8 @@ class lib:
data = self.d.analyze(probe_address, probe_data)
for i in data.keys():
if i == "read_power" or i == "write_power":
continue
data[i] = ch.round_time(data[i])
@@ -82,7 +84,7 @@ class lib:
self.lib.write(" current_unit : \"1mA\" ;\n")
self.lib.write(" resistance_unit : \"1kohm\" ;\n")
self.lib.write(" capacitive_load_unit(1 ,fF) ;\n")
self.lib.write(" leakage_power_unit : \"1uW\" ;\n")
self.lib.write(" leakage_power_unit : \"1mW\" ;\n")
self.lib.write(" pulling_resistance_unit :\"1kohm\" ;\n")
self.lib.write(" operating_conditions(TT){\n")
self.lib.write(" voltage : {0} ;\n".format(tech.spice["supply_voltage"]))
@@ -144,6 +146,13 @@ class lib:
self.lib.write(" index_1 (\"0.5\");\n")
self.lib.write(" }\n\n")
CONS2 = ["INPUT_BY_TRANS_FOR_CLOCK" , "INPUT_BY_TRANS_FOR_SIGNAL"]
for i in CONS2:
self.lib.write(" power_lut_template({0})".format(i))
self.lib.write("{\n")
self.lib.write(" variable_1 : input_transition_time;\n")
self.lib.write(" index_1 (\"0.5\");\n")
self.lib.write(" }\n\n")
def write_bus(self):
""" Adds format of DATA and ADDR bus."""
@@ -201,17 +210,36 @@ class lib:
self.lib.write(" pin(DATA[{0}:0])".format(self.word_size - 1))
self.lib.write("{\n")
self.lib.write(" }\n")
self.lib.write(" three_state : \"OEb & !clk\"; \n")
self.lib.write(" three_state : \"!OEb & !clk\"; \n")
self.lib.write(" memory_write(){ \n")
self.lib.write(" address : ADDR; \n")
self.lib.write(" clocked_on : clk; \n")
self.lib.write(" }\n")
self.write_timing(times)
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"OEb & !clk\"; \n")
self.lib.write(" rise_power(INPUT_BY_TRANS_FOR_SIGNAL){\n")
self.lib.write(" values(\"{0}\");\n".format(data["write_power"]* 1e3))
self.lib.write(" }\n")
self.lib.write(" fall_power(INPUT_BY_TRANS_FOR_SIGNAL){\n")
self.lib.write(" values(\"{0}\");\n".format(data["write_power"]* 1e3))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.write_timing(times)
self.lib.write(" memory_read(){ \n")
self.lib.write(" address : ADDR; \n")
self.lib.write(" }\n")
self.lib.write(" timing(){ \n")
self.lib.write(" internal_power(){\n")
self.lib.write(" when : \"!OEb & !clk\"; \n")
self.lib.write(" rise_power(INPUT_BY_TRANS_FOR_SIGNAL){\n")
self.lib.write(" values(\"{0}\");\n".format(data["read_power"]* 1e3))
self.lib.write(" }\n")
self.lib.write(" fall_power(INPUT_BY_TRANS_FOR_SIGNAL){\n")
self.lib.write(" values(\"{0}\");\n".format(data["read_power"]* 1e3))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" timing(){ \n")
self.lib.write(" timing_sense : non_unate; \n")
self.lib.write(" related_pin : \"clk\"; \n")
self.lib.write(" timing_type : rising_edge; \n")
@@ -267,26 +295,26 @@ class lib:
self.lib.write(" clock : true;\n")
self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["FF_in_cap"]))
self.lib.write(" min_pulse_width_high : {0} ; \n".format(ch.round_time(data["min_period1"])))
self.lib.write(" min_pulse_width_low : {0} ; \n".format(ch.round_time(data["min_period0"])))
min_pulse_width = (ch.round_time(data["min_period1"]) + ch.round_time(data["min_period0"]))/2.0
min_period = ch.round_time(data["min_period1"]) + ch.round_time(data["min_period0"])
self.lib.write(" timing(){ \n")
self.lib.write(" timing_type :\"min_pulse_width\"; \n")
self.lib.write(" related_pin : clk; \n")
self.lib.write(" rise_constraint(CLK_TRAN) {\n")
self.lib.write(" values(\"0\"); \n")
self.lib.write(" values(\"{0}\"); \n".format(min_pulse_width))
self.lib.write(" }\n")
self.lib.write(" fall_constraint(CLK_TRAN) {\n")
self.lib.write(" values(\"0\"); \n")
self.lib.write(" values(\"{0}\"); \n".format(min_pulse_width))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" timing(){ \n")
self.lib.write(" timing_type :\"minimum_period\"; \n")
self.lib.write(" related_pin : clk; \n")
self.lib.write(" rise_constraint(CLK_TRAN) {\n")
self.lib.write(" values(\"0\"); \n")
self.lib.write(" values(\"{0}\"); \n".format(min_period))
self.lib.write(" }\n")
self.lib.write(" fall_constraint(CLK_TRAN) {\n")
self.lib.write(" values(\"0\"); \n")
self.lib.write(" values(\"{0}\"); \n".format(min_period))
self.lib.write(" }\n")
self.lib.write(" }\n")
self.lib.write(" }\n")
+8 -10
View File
@@ -1,10 +1,8 @@
"""
This provides a set of useful generic types for the gdsMill interface.
"""
import tech
import debug
from utils import snap_to_grid
from vector import vector
class geometry:
@@ -38,7 +36,7 @@ class instance(geometry):
self.mod = mod
self.gds = mod.gds
self.rotate = rotate
self.offset = vector(snap_to_grid(offset))
self.offset = vector(offset).snap_to_grid()
self.mirror = mirror
debug.info(3, "creating instance: " + self.name)
@@ -72,7 +70,7 @@ class path(geometry):
self.name = "path"
self.layerNumber = layerNumber
self.coordinates = map(lambda x: [x[0], x[1]], coordinates)
self.coordinates = snap_to_grid(self.coordinates)
self.coordinates = vector(self.coordinates).snap_to_grid()
self.path_width = path_width
# FIXME figure out the width/height. This type of path is not
@@ -105,7 +103,7 @@ class label(geometry):
self.name = "label"
self.text = text
self.layerNumber = layerNumber
self.offset = vector(snap_to_grid(offset))
self.offset = vector(offset).snap_to_grid()
self.zoom = zoom
self.size = 0
@@ -137,10 +135,10 @@ class rectangle(geometry):
geometry.__init__(self)
self.name = "rect"
self.layerNumber = layerNumber
self.offset = vector(snap_to_grid(offset))
self.size = snap_to_grid([width, height])
self.width = self.size[0]
self.height = self.size[1]
self.offset = vector(offset).snap_to_grid()
self.size = vector(width, height).snap_to_grid()
self.width = self.size.x
self.height = self.size.y
debug.info(3, "creating rectangle (" + str(self.layerNumber) + "): "
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
@@ -148,7 +146,7 @@ class rectangle(geometry):
def gds_write_file(self, newLayout):
"""Writes the rectangular shape to GDS"""
debug.info(3, "writing rectangle (" + str(self.layerNumber) + "): "
debug.info(3, "writing rectangle (" + str(self.layerNumber) + "):"
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
newLayout.addBox(layerNumber=self.layerNumber,
purposeNumber=0,
+132 -44
View File
@@ -3,8 +3,6 @@ import design
import math
from tech import drc
from contact import contact
from nand_2 import nand_2
from nand_3 import nand_3
from pinv import pinv
from vector import vector
from globals import OPTS
@@ -41,14 +39,8 @@ class hierarchical_predecode(design.design):
beta=2,
height=self.bitcell_height)
self.add_mod(self.inv)
if self.number_of_inputs ==2:
self.nand = nand_2(name="a_nand_2",
nmos_width=self.nmos_width,
height=self.bitcell_height)
elif self.number_of_inputs ==3:
self.nand = nand_3(name="a_nand_3",
nmos_width=self.nmos_width,
height=self.bitcell_height)
# create_nand redefine in sub class based on number of inputs
self.create_nand()
self.add_mod(self.nand)
def set_up_constrain(self):
@@ -75,12 +67,8 @@ class hierarchical_predecode(design.design):
self.gap_between_rail_offset = self.gap_between_rails + drc["minwidth_metal2"]
self.rails_x_offset = []
if self.number_of_inputs == 2:
self.rail_height = (self.number_of_outputs * self.nand.height
- (self.number_of_outputs - 1) * drc["minwidth_metal2"])
elif self.number_of_inputs == 3:
self.rail_height = (self.number_of_outputs * self.nand.height
- 1.5 * drc["minwidth_metal2"])
# set_rail_height redefine in sub class
self.set_rail_height()
# Creating the left hand side metal2 rails for input connections
for hrail_1 in range(self.number_of_inputs):
xoffset_1 = (self.metal2_extend_contact
@@ -100,10 +88,7 @@ class hierarchical_predecode(design.design):
def update_size(self):
self.width = self.x_off_inv_2 + self.inv.width
if self.number_of_inputs ==2:
self.height = 4 * self.nand.height
elif self.number_of_inputs ==3:
self.height = 8 * self.nand.height
self.set_height()
self.size = vector(self.width, self.height)
correct =vector(0, 0.5 * drc["minwidth_metal1"])
self.vdd_position = self.size - correct - vector(0, self.inv.height)
@@ -142,10 +127,8 @@ class hierarchical_predecode(design.design):
def add_nand(self,connections):
for nand_input in range(self.number_of_outputs):
if self.number_of_inputs ==2:
name = "Xpre2x4_nand[{0}]".format(nand_input)
elif self.number_of_inputs ==3:
name = "Xpre3x8_nand[{0}]".format(nand_input)
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
name = "Xpre"+inout+"_nand[{0}]".format(nand_input)
if (nand_input % 2 == 0):
y_off = nand_input * (self.nand.height)
mirror = "R0"
@@ -167,30 +150,135 @@ class hierarchical_predecode(design.design):
self.connect_inst(connections[nand_input])
def route(self):
# route sub funtions need to be redfined in sub class
self.route_input_inverters()
self.route_nand_to_rails()
self.route_vdd_gnd_from_rails_to_gates()
def route_input_inverters_input(self,inv_rout,inv_in_offset):
def route_input_inverters(self):
# All conections of the inputs inverters [Inputs, outputs, vdd, gnd]
output_shift = self.set_output_shift()
for inv_rout in range(self.number_of_inputs):
setup = self.setup_route_input_inverter(inv_rout,output_shift)
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
#add output
correct = y_dir * (output_shift + drc["minwidth_metal1"])
output_metal = self.cal_input_inverters_output(setup,output_shift,inv_rout)
offset1,offset2=output_metal[0]
offset3,offset4=output_metal[1]
self.add_rect(layer="metal1",
offset=offset1,
width=drc["minwidth_metal1"],
height=offset2.y - offset1.y)
self.add_rect(layer="metal1",
offset=offset3,
width=offset4.x - offset3.x,
height=drc["minwidth_metal1"])
off_via = [self.rails_x_offset[inv_rout + self.number_of_inputs+2] + self.gap_between_rails,
inv_vdd_offset.y- self.via_shift - correct]
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=off_via,
rotate=90)
#route input
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[inv_rout],
inv_in_offset.y],
width=inv_in_offset.x - self.rails_x_offset[inv_rout] + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[inv_rout] + self.gap_between_rails,
inv_in_offset.y - self.via_shift],
rotate=90)
# route vdd
self.add_rect(layer="metal1",
offset=inv_vdd_offset,
width=self.rails_x_offset[self.number_of_inputs] - inv_vdd_offset.x + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
# route gnd
self.add_rect(layer="metal1",
offset=inv_gnd_offset,
width=self.rails_x_offset[self.number_of_inputs+1] - inv_gnd_offset.x + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
def setup_route_input_inverter(self, inv_rout, output_shift):
# add Inputs, vdd, gnd of the inputs inverters
if (inv_rout % 2 == 0):
base_offset=[self.x_off_inv_1, inv_rout * self.inv.height ]
y_dir = 1
else:
base_offset=[self.x_off_inv_1, 2 * self.inv.height - drc["minwidth_metal1"]]
y_dir = -1
inv_out_offset = base_offset+self.inv.Z_position.scale(1,y_dir)
inv_in_offset = base_offset+self.inv.A_position.scale(1,y_dir)
inv_vdd_offset = base_offset+self.inv.vdd_position.scale(1,y_dir)
inv_gnd_offset = base_offset+self.inv.gnd_position.scale(1,y_dir)
#return info to create output of the input inverter
return [y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset]
def route_nand_to_rails(self):
# This 2D array defines the connection mapping
nand_input_line_combination = self.get_nand_input_line_combination()
for k in range(self.number_of_outputs):
# create x offset list
index_lst= nand_input_line_combination[k]
line_x_offset = []
for index in index_lst:
line_x_offset.append(self.rails_x_offset[index])
# create y offset list
yoffset_nand_in, correct= self.create_y_offsets(k)
# connect based on the two list
for i in range(self.number_of_inputs):
x_offset = line_x_offset[i]
y_offset = yoffset_nand_in[i]
# Connecting the i-th input of Nand3 gate
self.add_rect(layer="metal1",
offset=[x_offset, y_offset],
width=self.x_off_nand - x_offset,
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[x_offset+ self.gap_between_rails,
y_offset - self.via_shift - correct[i]],
rotate=90)
# Extended of the top NAND2 to the left hand side input rails
if(k == self.number_of_outputs - 1):
x_offset = self.rails_x_offset[i]
self.add_rect(layer="metal1",
offset=[x_offset, y_offset],
width=self.x_off_nand - x_offset,
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[x_offset + self.gap_between_rails,
y_offset - self.via_shift],
rotate=90)
def route_vdd_gnd_from_rails_to_gates(self):
via_correct = self.get_via_correct()
for k in range(self.number_of_outputs):
power_line_index = self.number_of_inputs + 1 - (k%2)
yoffset = k * self.inv.height - 0.5 * drc["minwidth_metal1"]
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[power_line_index],
yoffset],
width=self.x_off_nand - self.rails_x_offset[power_line_index],
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[power_line_index] + self.gap_between_rails,
yoffset - via_correct.y],
rotate=90)
yoffset = (self.number_of_outputs * self.inv.height
- 0.5 * drc["minwidth_metal1"])
v_metal = self.get_vertical_metal()
via_y = self.get_via_y()
index = self.number_of_inputs + 1
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[inv_rout],
inv_in_offset.y],
width=inv_in_offset.x - self.rails_x_offset[inv_rout] + drc["minwidth_metal2"],
offset=[self.rails_x_offset[index], yoffset],
width=self.x_off_nand - self.rails_x_offset[index],
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[inv_rout] + self.gap_between_rails,
inv_in_offset.y - self.via_shift],
self.add_rect(layer=v_metal,
offset=[self.rails_x_offset[index], self.rail_height],
width=drc["minwidth_"+v_metal],
height=yoffset - self.rail_height)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[index] + self.gap_between_rails,
via_y] - via_correct,
rotate=90)
def route_input_inverters_vdd(self,inv_vdd_offset):
self.add_rect(layer="metal1",
offset=inv_vdd_offset,
width=self.rails_x_offset[self.number_of_inputs] - inv_vdd_offset.x + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
def route_input_inverters_gnd(self,inv_gnd_offset):
self.add_rect(layer="metal1",
offset=inv_gnd_offset,
width=self.rails_x_offset[self.number_of_inputs+1] - inv_gnd_offset.x + drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
+56 -105
View File
@@ -1,6 +1,7 @@
from tech import drc
import debug
import design
from nand_2 import nand_2
from vector import vector
from hierarchical_predecode import hierarchical_predecode
@@ -8,7 +9,6 @@ class hierarchical_predecode2x4(hierarchical_predecode):
"""
Pre 2x4 decoder used in hierarchical_decoder.
"""
def __init__(self, nmos_width, cellname):
hierarchical_predecode.__init__(self, nmos_width, cellname, 2)
@@ -18,6 +18,15 @@ class hierarchical_predecode2x4(hierarchical_predecode):
self.create_layout()
self.route()
def create_nand(self):
self.nand = nand_2(name="a_nand_2",
nmos_width=self.nmos_width,
height=self.bitcell_height)
def set_rail_height(self):
self.rail_height = (self.number_of_outputs * self.nand.height
- (self.number_of_outputs - 1) * drc["minwidth_metal2"])
def create_layout(self):
self.create_rails()
self.add_inv2x4()
@@ -28,6 +37,9 @@ class hierarchical_predecode2x4(hierarchical_predecode):
["B[0]", "B[1]", "Z[0]", "vdd", "gnd"]]
self.add_nand(connections)
def set_height(self):
self.height = 4 * self.nand.height
def add_inv2x4(self):
self.A_positions = []
for inv_2x4 in range(self.number_of_inputs):
@@ -51,109 +63,48 @@ class hierarchical_predecode2x4(hierarchical_predecode):
"B[{0}]".format(inv_2x4),
"vdd", "gnd"])
def route_input_inverters(self):
# All conections of the inputs inverters [Inputs, outputs, vdd, gnd]
output_shift = 2 * drc["minwidth_metal1"]
for inv_rout in range(self.number_of_inputs):
if (inv_rout % 2 == 0):
y_dir= 1
else:
y_dir= -1
base = vector(self.x_off_inv_1,
(1-y_dir) * (self.inv.height - 0.5 * drc["minwidth_metal1"]))
inv_out_offset = base + self.inv.Z_position.scale(1,y_dir)
inv_in_offset = base + self.inv.A_position.scale(1,y_dir)
inv_vdd_offset = base + self.inv.vdd_position.scale(1,y_dir)
inv_gnd_offset = base + self.inv.gnd_position.scale(1,y_dir)
out_y_mirrored = inv_vdd_offset.y+ output_shift + drc["minwidth_metal1"]
out_offset = [inv_out_offset.x,
inv_out_offset.y* (1 + y_dir) / 2
+ out_y_mirrored * (1 - y_dir) / 2]
# output connection
correct = y_dir * (output_shift + drc["minwidth_metal1"])
off_via = [self.rails_x_offset[inv_rout + 4] + self.gap_between_rails,
inv_vdd_offset.y- self.via_shift - correct]
self.add_rect(layer="metal1",
offset=out_offset,
width=drc["minwidth_metal1"],
height=(inv_vdd_offset.y- inv_out_offset.y) * y_dir - output_shift)
self.add_rect(layer="metal1",
offset=[inv_out_offset.x,
inv_vdd_offset.y- correct],
width=self.rails_x_offset[inv_rout + 4] - inv_out_offset.x+ drc["minwidth_metal2"],
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=off_via,
rotate=90)
self.route_input_inverters_input(inv_rout,inv_in_offset)
self.route_input_inverters_vdd(inv_vdd_offset)
self.route_input_inverters_gnd(inv_gnd_offset)
def cal_input_inverters_output(self,setup,output_shift,inv_rout):
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
correct = y_dir * (output_shift + drc["minwidth_metal1"])
out_offset = vector(inv_out_offset)
if y_dir == -1:
out_offset.y = inv_vdd_offset.y + output_shift + drc["minwidth_metal1"]
def route_nand_to_rails(self):
# This 2D array defines the connection mapping
nand2_input_line_combination = [[4, 5], [6, 5], [4, 7], [6, 7]]
for k in range(self.number_of_outputs):
# create x offset list
x_index = nand2_input_line_combination[k]
line_x_offset = [self.rails_x_offset[x_index[0]],
self.rails_x_offset[x_index[1]]]
# create y offset list
if (k % 2 == 0):
y_off = k * (self.nand.height)
direct = 1
else:
y_off = (k + 1) * (self.nand.height) - drc["minwidth_metal1"]
direct = - 1
list_connect = [y_off + direct * self.nand.A_position.y,
y_off + direct * self.nand.B_position.y]
# connect based on the two list
for connect in list_connect:
x_offset = line_x_offset[list_connect.index(connect)]
self.add_rect(layer="metal1",
offset=[x_offset, connect],
width=self.x_off_nand - x_offset,
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[x_offset + self.gap_between_rails,
connect - self.via_shift],
rotate=90)
# Extended of the top NAND2 to the left hand side input rails
if(k == self.number_of_outputs - 1):
x_offset = self.rails_x_offset[list_connect.index(connect)]
self.add_rect(layer="metal1",
offset=[x_offset, connect],
width=self.x_off_nand - x_offset,
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[x_offset + self.gap_between_rails,
connect - self.via_shift],
rotate=90)
vertical1 = out_offset
vertical2 = vertical1 + vector(0,
(inv_vdd_offset.y - inv_out_offset.y) * y_dir
- output_shift)
horizontal1 = vector(inv_out_offset.x,
inv_vdd_offset.y - correct)
horizontal2 = horizontal1 + vector(self.rails_x_offset[inv_rout + 4] - inv_out_offset.x+ drc["minwidth_metal2"],
0)
return [[vertical1,vertical2],[horizontal1,horizontal2]]
def route_vdd_gnd_from_rails_to_gates(self):
for k in range(self.number_of_outputs):
power_line_index = 3 - (k%2)
yoffset = k * self.inv.height - 0.5 * drc["minwidth_metal1"]
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[power_line_index],
yoffset],
width=self.x_off_nand - self.rails_x_offset[power_line_index],
height=drc["minwidth_metal1"])
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[power_line_index] + self.gap_between_rails,
yoffset - self.via_shift],
rotate=90)
yoffset = (self.number_of_outputs * self.inv.height
- 0.5 * drc["minwidth_metal1"])
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[3], yoffset],
width=self.x_off_nand - self.rails_x_offset[3],
height=drc["minwidth_metal1"])
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[3], self.rail_height],
width=drc["minwidth_metal1"],
height=yoffset - self.rail_height)
self.add_via(layers = ("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[3] + self.gap_between_rails,
self.rail_height - self.via_shift],
rotate=90)
def set_output_shift(self):
return 2 * drc["minwidth_metal1"]
def get_nand_input_line_combination(self):
combination = [[4, 5], [6, 5], [4, 7], [6, 7]]
return combination
def create_y_offsets(self,k):
# create y offset list
if (k % 2 == 0):
y_off = k * (self.nand.height)
direct = 1
else:
y_off = (k + 1) * (self.nand.height) - drc["minwidth_metal1"]
direct = - 1
correct =[0,0]
yoffset_nand_in = [y_off + direct * self.nand.A_position.y,
y_off + direct * self.nand.B_position.y]
return yoffset_nand_in, correct
def get_via_correct(self):
return vector(0, self.via_shift)
def get_vertical_metal(self):
return "metal1"
def get_via_y(self):
return self.rail_height
+51 -100
View File
@@ -1,6 +1,7 @@
from tech import drc
import debug
import design
from nand_3 import nand_3
from vector import vector
from hierarchical_predecode import hierarchical_predecode
@@ -18,6 +19,14 @@ class hierarchical_predecode3x8(hierarchical_predecode):
self.create_layout()
self.route()
def create_nand(self):
self.nand = nand_3(name="a_nand_3",
nmos_width=self.nmos_width,
height=self.bitcell_height)
def set_rail_height(self):
self.rail_height = (self.number_of_outputs * self.nand.height
- 1.5 * drc["minwidth_metal2"])
def create_layout(self):
self.create_rails()
self.add_output_inverters()
@@ -31,111 +40,53 @@ class hierarchical_predecode3x8(hierarchical_predecode):
["B[0]", "B[1]", "B[2]", "Z[0]", "vdd", "gnd"]]
self.add_nand(connections)
def route_input_inverters(self):
# All conections of the inputs inverters [Inputs, outputs, vdd, gnd]
for inv_rout in range(self.number_of_inputs):
output_shift = 1.5 * drc["minwidth_metal1"]
def set_height(self):
self.height = 8 * self.nand.height
if (inv_rout % 2 == 0):
base_offset=[self.x_off_inv_1, inv_rout * self.inv.height ]
y_dir = 1
else:
base_offset=[self.x_off_inv_1, 2 * self.inv.height - drc["minwidth_metal1"]]
y_dir = -1
inv_out_offset = base_offset+self.inv.Z_position.scale(1,y_dir)
inv_in_offset = base_offset+self.inv.A_position.scale(1,y_dir)
inv_vdd_offset = base_offset+self.inv.vdd_position.scale(1,y_dir)
inv_gnd_offset = base_offset+self.inv.gnd_position.scale(1,y_dir)
# output connection
correct = y_dir * (output_shift + drc["minwidth_metal1"])
off_via = [self.rails_x_offset[inv_rout + 5] + self.gap_between_rails,
inv_vdd_offset.y - self.via_shift - correct]
path1 = inv_out_offset + vector(0.5*drc["minwidth_metal1"],
- 1.5*drc["minwidth_metal1"] - correct)
path2 = vector(path1.x,
inv_vdd_offset.y + 0.5 * drc["minwidth_metal1"] - correct)
path3 = vector(self.rails_x_offset[inv_rout + 5] + drc["minwidth_metal2"],
path2.y)
self.add_path("metal1", [path1,path2,path3])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=off_via,
rotate=90)
self.route_input_inverters_input(inv_rout,inv_in_offset)
self.route_input_inverters_vdd(inv_vdd_offset)
self.route_input_inverters_gnd(inv_gnd_offset)
def cal_input_inverters_output(self,setup,output_shift,inv_rout):
y_dir,inv_in_offset,inv_out_offset,inv_vdd_offset,inv_gnd_offset = setup
correct = y_dir * (output_shift + drc["minwidth_metal1"])
def route_nand_to_rails(self):
# This 2D array defines the connection mapping of the Nand3 gates to
# the rail
nand3_input_line_combination = [[5, 6, 7], [5, 6, 10],
[5, 9, 7], [5, 9, 10],
[8, 6, 7], [8, 6, 10],
[8, 9, 7], [8, 9, 10]]
for k in range(self.number_of_outputs):
index_lst = nand3_input_line_combination[k]
line_x_offset = []
for index in index_lst:
line_x_offset.append(self.rails_x_offset[index])
out_offset = inv_out_offset + vector(0, output_shift + correct)
vertical1 = out_offset
vertical2 = (vertical1.scale(1, 0) + inv_vdd_offset.scale(0, 1)
+ vector(0, - correct))
horizontal1 = vertical1
horizontal2 = vector(self.rails_x_offset[inv_rout + 5] + drc["minwidth_metal2"],
vertical2.y)
return [[vertical1,vertical2],[horizontal1,horizontal2]]
if (k % 2 == 0):
y_off = k * (self.nand.height)
y_dir =1
correct = [0,0,self.contact_shift]
else:
y_off = 2 * self.inv.height - drc["minwidth_metal1"] + (k - 1) * (self.nand.height)
y_dir = -1
correct = [0,self.contact_shift,0]
yoffset_nand_in = [y_off + y_dir*self.nand.A_position[1],
y_off + y_dir*self.nand.B_position[1],
y_off + y_dir*self.nand.C_position[1]]
def set_output_shift(self):
return 1.5 * drc["minwidth_metal1"]
for i in range(self.number_of_inputs):
# Connecting the i-th input of Nand3 gate
self.add_rect(layer="metal1",
offset=[line_x_offset[i], yoffset_nand_in[i]],
width=self.x_off_nand - line_x_offset[i],
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[line_x_offset[i]+ self.gap_between_rails,
yoffset_nand_in[i] - self.via_shift - correct[i]],
rotate=90)
#Extended of the top NAND2 to the left hand side input rails
if(k == self.number_of_outputs - 1):
for i in range(self.number_of_inputs):
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[i], yoffset_nand_in[i]],
width=self.x_off_nand - self.rails_x_offset[i],
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[i] + self.gap_between_rails,
yoffset_nand_in[i] - self.via_shift],
rotate=90)
def get_nand_input_line_combination(self):
combination = [[5, 6, 7], [5, 6, 10],
[5, 9, 7], [5, 9, 10],
[8, 6, 7], [8, 6, 10],
[8, 9, 7], [8, 9, 10]]
return combination
def route_vdd_gnd_from_rails_to_gates(self):
for k in range(self.number_of_outputs):
power_line_index = 4 - (k%2)
yoffset = k * self.inv.height - 0.5 * drc["minwidth_metal1"]
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[power_line_index], yoffset],
width=self.x_off_nand - self.rails_x_offset[power_line_index],
height=drc["minwidth_metal1"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[power_line_index] + self.gap_between_rails,
yoffset - self.via_shift - self.contact_shift],
rotate=90)
def create_y_offsets(self,k):
if (k % 2 == 0):
y_off = k * (self.nand.height)
y_dir =1
correct = [0,0,self.contact_shift]
else:
y_off = 2 * self.inv.height - drc["minwidth_metal1"] + (k - 1) * (self.nand.height)
y_dir = -1
correct = [0,self.contact_shift,0]
yoffset_nand_in = [y_off + y_dir*self.nand.A_position[1],
y_off + y_dir*self.nand.B_position[1],
y_off + y_dir*self.nand.C_position[1]]
return yoffset_nand_in, correct
def get_via_correct(self):
return vector(0, self.via_shift+self.contact_shift)
def get_vertical_metal(self):
return "metal2"
def get_via_y(self):
yoffset = (self.number_of_outputs * self.inv.height
- 0.5 * drc["minwidth_metal1"])
self.add_rect(layer="metal1",
offset=[self.rails_x_offset[4], yoffset],
width=self.x_off_nand - self.rails_x_offset[4],
height=drc["minwidth_metal1"])
self.add_rect(layer="metal2",
offset=[self.rails_x_offset[4], self.rail_height],
width=drc["minwidth_metal2"],
height=yoffset - self.rail_height)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=[self.rails_x_offset[4] + self.gap_between_rails - self.via_shift,
yoffset - self.via_shift - self.contact_shift],
rotate=90)
return yoffset
+2 -3
View File
@@ -5,7 +5,6 @@ import debug
from tech import drc, GDS
from tech import layer as techlayer
import os
from utils import snap_to_grid
from vector import vector
class layout:
@@ -96,9 +95,9 @@ class layout:
"""Translates all 2d cartesian coordinates in a layout given
the (x,y) offset"""
for obj in self.objs:
obj.offset = vector(snap_to_grid(obj.offset - coordinate))
obj.offset = vector(obj.offset - coordinate)
for inst in self.insts:
inst.offset = vector(snap_to_grid(inst.offset - coordinate))
inst.offset = vector(inst.offset - coordinate)
# FIXME: Make name optional and pick a random one if not specified
def add_inst(self, name, mod, offset=[0,0], mirror="R0",rotate=0):
+3 -2
View File
@@ -4,7 +4,7 @@ Check the .lib file for an SRAM
"""
import unittest
from testutils import header,isdiff
from testutils import header,isapproxdiff
import sys,os
sys.path.append(os.path.join(sys.path[0],".."))
import globals
@@ -40,7 +40,8 @@ class lib_test(unittest.TestCase):
# let's diff the result with a golden model
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
self.assertEqual(isdiff(libname,golden),True)
# Randomly decided 10% difference between spice simulators is ok.
self.assertEqual(isapproxdiff(libname,golden,0.10),True)
os.system("rm {0}".format(libname))
@@ -5,7 +5,7 @@ library (sram_2_16_1_freepdk45_lib){
current_unit : "1mA" ;
resistance_unit : "1kohm" ;
capacitive_load_unit(1 ,fF) ;
leakage_power_unit : "1uW" ;
leakage_power_unit : "1mW" ;
pulling_resistance_unit :"1kohm" ;
operating_conditions(TT){
voltage : 1.0 ;
@@ -69,6 +69,16 @@ library (sram_2_16_1_freepdk45_lib){
index_1 ("0.5");
}
power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){
variable_1 : input_transition_time;
index_1 ("0.5");
}
power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){
variable_1 : input_transition_time;
index_1 ("0.5");
}
default_operating_conditions : TT;
@@ -106,11 +116,20 @@ cell (sram_2_16_1_freepdk45){
max_capacitance : 0.62166;
pin(DATA[1:0]){
}
three_state : "OEb & !clk";
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
internal_power(){
when : "OEb & !clk";
rise_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("0.66109");
}
fall_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("0.66109");
}
}
timing(){
timing_type : setup_rising;
related_pin : "clk";
@@ -134,6 +153,15 @@ cell (sram_2_16_1_freepdk45){
memory_read(){
address : ADDR;
}
internal_power(){
when : "!OEb & !clk";
rise_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("0.027754");
}
fall_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("0.027754");
}
}
timing(){
timing_sense : non_unate;
related_pin : "clk";
@@ -262,26 +290,24 @@ cell (sram_2_16_1_freepdk45){
clock : true;
direction : input;
capacitance : 0.2091;
min_pulse_width_high : 0.081 ;
min_pulse_width_low : 0.267 ;
timing(){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(CLK_TRAN) {
values("0");
values("0.174");
}
fall_constraint(CLK_TRAN) {
values("0");
values("0.174");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(CLK_TRAN) {
values("0");
values("0.348");
}
fall_constraint(CLK_TRAN) {
values("0");
values("0.348");
}
}
}
@@ -5,7 +5,7 @@ library (sram_2_16_1_scn3me_subm_lib){
current_unit : "1mA" ;
resistance_unit : "1kohm" ;
capacitive_load_unit(1 ,fF) ;
leakage_power_unit : "1uW" ;
leakage_power_unit : "1mW" ;
pulling_resistance_unit :"1kohm" ;
operating_conditions(TT){
voltage : 5.0 ;
@@ -69,6 +69,16 @@ library (sram_2_16_1_scn3me_subm_lib){
index_1 ("0.5");
}
power_lut_template(INPUT_BY_TRANS_FOR_CLOCK){
variable_1 : input_transition_time;
index_1 ("0.5");
}
power_lut_template(INPUT_BY_TRANS_FOR_SIGNAL){
variable_1 : input_transition_time;
index_1 ("0.5");
}
default_operating_conditions : TT;
@@ -106,11 +116,20 @@ cell (sram_2_16_1_scn3me_subm){
max_capacitance : 11.3222;
pin(DATA[1:0]){
}
three_state : "OEb & !clk";
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
internal_power(){
when : "OEb & !clk";
rise_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("15.6576");
}
fall_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("15.6576");
}
}
timing(){
timing_type : setup_rising;
related_pin : "clk";
@@ -134,6 +153,15 @@ cell (sram_2_16_1_scn3me_subm){
memory_read(){
address : ADDR;
}
internal_power(){
when : "!OEb & !clk";
rise_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("6.2822");
}
fall_power(INPUT_BY_TRANS_FOR_SIGNAL){
values("6.2822");
}
}
timing(){
timing_sense : non_unate;
related_pin : "clk";
@@ -262,26 +290,24 @@ cell (sram_2_16_1_scn3me_subm){
clock : true;
direction : input;
capacitance : 9.8242;
min_pulse_width_high : 1.658 ;
min_pulse_width_low : 3.428 ;
timing(){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(CLK_TRAN) {
values("0");
values("2.543");
}
fall_constraint(CLK_TRAN) {
values("0");
values("2.543");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(CLK_TRAN) {
values("0");
values("5.086");
}
fall_constraint(CLK_TRAN) {
values("0");
values("5.086");
}
}
}
+73 -1
View File
@@ -1,5 +1,4 @@
def isclose(value1,value2,error_tolerance=1e-2):
""" This is used to compare relative values. """
import debug
@@ -11,6 +10,79 @@ def isclose(value1,value2,error_tolerance=1e-2):
debug.info(2,"CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff))
return (check)
def relative_compare(value1,value2,error_tolerance):
""" This is used to compare relative values. """
if (value1==value2): # if we don't need a relative comparison!
return True
return (abs(value1 - value2) / max(value1,value2) <= error_tolerance)
def isapproxdiff(f1, f2, error_tolerance=0.001):
"""Compare two files.
Arguments:
f1 -- First file name
f2 -- Second file name
Return value:
True if the files are the same, False otherwise.
"""
import re
import debug
with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2:
while True:
b1 = fp1.readline()
b2 = fp2.readline()
#print "b1:",b1,
#print "b2:",b2,
# 1. Find all of the floats using a regex
numeric_const_pattern = r"""
[-+]? # optional sign
(?:
(?: \d* \. \d+ ) # .1 .12 .123 etc 9.1 etc 98.1 etc
|
(?: \d+ \.? ) # 1. 12. 123. etc 1 12 123 etc
)
# followed by optional exponent part if desired
(?: [Ee] [+-]? \d+ ) ?
"""
rx = re.compile(numeric_const_pattern, re.VERBOSE)
b1_floats=rx.findall(b1)
b2_floats=rx.findall(b2)
debug.info(3,"b1_floats: "+str(b1_floats))
debug.info(3,"b2_floats: "+str(b2_floats))
# 2. Remove the floats from the string
for f in b1_floats:
b1=b1.replace(str(f),"")
for f in b2_floats:
b2=b2.replace(str(f),"")
#print "b1:",b1,
#print "b2:",b2,
# 3. Check if remaining string matches
if b1 != b2:
debug.info(2,"Line: {0}\n!=\nLine: {1}".format(b1,b2))
return False
# 4. Now compare that the floats match
if len(b1_floats)!=len(b2_floats):
debug.info(2,"Len {0} != {1}".format(len(b1_floats),len(b2_floats)))
return False
for (f1,f2) in zip(b1_floats,b2_floats):
if not relative_compare(float(f1),float(f2),error_tolerance):
debug.info(2, "Float {0} != {1}".format(f1,f2))
return False
if not b1:
return True
def isdiff(file1,file2):
""" This is used to compare two files and display the diff if they are different.. """
import debug
+4 -1
View File
@@ -5,6 +5,7 @@ import globals
OPTS = globals.OPTS
<<<<<<< HEAD
def snap_to_grid(offset):
"""
Changes the coodrinate to match the grid settings
@@ -25,10 +26,12 @@ def gds_pin_center(gdsPin):
"""
This returns the center of a pin shape
"""
=======
def gdsPinToOffset(gdsPin):
>>>>>>> master
boundary = gdsPin[2]
return [0.5 * (boundary[0] + boundary[2]), 0.5 * (boundary[1] + boundary[3])]
def auto_measure_libcell(pin_list, name, units, layer):
"""
Open a GDS file and find the pins in pin_list as text on a given layer.
+16
View File
@@ -1,5 +1,6 @@
import debug
import math
import tech
class vector():
"""
@@ -83,6 +84,21 @@ class vector():
"""
return vector(other[0]- self.x, other[1] - self.y)
def snap_to_grid(self):
self.x = self.snap_offset_to_grid(self.x)
self.y = self.snap_offset_to_grid(self.y)
return self
def snap_offset_to_grid(self, offset):
"""
Changes the coodrinate to match the grid settings
"""
grid = tech.drc["grid"]
# this gets the nearest integer value
off_in_grid = int(round(round((offset / grid), 2), 0))
offset = off_in_grid * grid
return offset
def rotate(self):
""" pass a copy of rotated vector, without altering the vector! """
return vector(self.y,self.x)