OpenRAM/compiler/logic_effort_dc.py

190 lines
7.8 KiB
Python
Raw Normal View History

2016-11-08 18:57:35 +01:00
import debug
import design
from tech import drc
2016-11-08 18:57:35 +01:00
from pinv import pinv
from contact import contact
from vector import vector
from globals import OPTS
class logic_effort_dc(design.design):
"""
Generate a logic effort based delay chain.
Input is a list contains the electrical effort of each stage.
"""
def __init__(self, name, stage_list):
"""init function"""
design.design.__init__(self, "delay_chain")
#fix me: input should be logic effort value
# and there should be functions to get
# area effecient inverter stage list
# chain_length is number of inverters in the load
# plus 1 for the input
chain_length = 1 + sum(stage_list)
# half chain length is the width of the layeout
# invs are stacked into 2 levels so input/output are close
half_length = round(chain_length / 2.0)
c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell_height = self.mod_bitcell.chars["height"]
self.add_pins()
self.create_module()
self.cal_cell_size(half_length)
self.create_inv_stage_lst(stage_list)
self.add_inv_lst(chain_length)
self.route_inv(stage_list)
self.add_vddgnd_label()
self.DRC_LVS()
def add_pins(self):
"""add the pins of the delay chain"""
self.add_pin("clk_in")
self.add_pin("clk_out")
self.add_pin("vdd")
self.add_pin("gnd")
def cal_cell_size(self, half_length):
""" calculate the width and height of the cell"""
self.width = half_length * self.inv.width
self.height = 2 * self.bitcell_height
def create_module(self):
"""add the inverters"""
self.inv = pinv(name="delay_chain_inv",
nmos_width=drc["minwidth_tx"],
route_output=False)
self.add_mod(self.inv)
def create_inv_stage_lst(self, stage_list):
""" Generate a list to indicate what stage each inv is in """
self.inv_stage_lst = [[0, True]]
stage_num = 0
for stage in stage_list:
stage_num = stage_num + 1
repeat_times = stage
for i in range(repeat_times):
if i == repeat_times - 1:
# the first one need to connected to the next stage
self.inv_stage_lst.append([stage_num, True])
else:
# the rest should not drive any thing
self.inv_stage_lst.append([stage_num, False])
def add_inv_lst(self, chain_length):
"""add the inverter and connect them based on the stage list """
half_length = round(chain_length / 2.0)
self.inv_positions = []
for i in range(chain_length):
if i < half_length:
# add top level
inv_offset = [i * self.inv.width,
2 * self.inv.height]
inv_mirror="MX"
self.inv_positions.append(inv_offset)
offset = inv_offset + \
self.inv.input_position.scale(1,-1)
m1m2_via_rotate=270
if i == 0:
self.clk_in_offset = offset
else:
# add bottom level
inv_offset = [(chain_length - i) * self.inv.width,
0]
inv_mirror="MY"
self.inv_positions.append(inv_offset)
offset = inv_offset + \
self.inv.input_position.scale(-1,1)
m1m2_via_rotate=90
if i == chain_length - 1:
self.clk_out_offset = inv_offset + \
self.inv.output_position.scale(-1,1)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset,
rotate=m1m2_via_rotate)
self.add_inst(name="inv_chain%d" % i,
mod=self.inv,
offset=inv_offset,
mirror=inv_mirror)
# connecting spice
if i == 0:
self.connect_inst(args=["clk_in", "s" + str(self.inv_stage_lst[i][0] + 1),
"vdd", "gnd"],
check=False)
spare_node_counter = 1
elif i == chain_length - 1:
self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]), "clk_out", "vdd", "gnd"],
check=False)
else:
if self.inv_stage_lst[i][1] == True:
self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]),
"s" + str(self.inv_stage_lst[i][0] + 1), "vdd", "gnd"],
check=False)
spare_node_counter = 1
else:
self.connect_inst(args=["s" + str(self.inv_stage_lst[i][0]), "s" \
+ str(self.inv_stage_lst[i][0] + 1) + "n" \
+ str(spare_node_counter), "vdd", "gnd"],
check=False)
spare_node_counter += 1
def route_inv(self, stage_list):
"""add metal routing based on the stage list """
half_length = round((sum(stage_list) + 1) / 2.0)
start_inv = end_inv = 0
for stage in stage_list:
# end inv number depends on the fan out number
end_inv = start_inv + stage
start_inv_offset = self.inv_positions[start_inv]
end_inv_offset = self.inv_positions[end_inv]
if start_inv < half_length:
start_o_offset = start_inv_offset + \
self.inv.output_position.scale(1,-1)
m1m2_via_rotate =270
m1m2_via_vc = vector(1,-.5)
else:
start_o_offset = start_inv_offset + \
self.inv.output_position.scale(-1,1)
m1m2_via_rotate =90
m1m2_via_vc = vector(1,.5)
M2_start = start_o_offset + vector(0,drc["minwidth_metal2"]).scale(m1m2_via_vc)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=start_o_offset,
rotate=m1m2_via_rotate)
if end_inv < half_length:
end_i_offset = end_inv_offset + \
self.inv.input_position.scale(1,-1)
M2_end = end_i_offset - vector(0, 0.5 * drc["minwidth_metal2"])
2016-11-08 18:57:35 +01:00
else:
end_i_offset = end_inv_offset + \
self.inv.input_position.scale(-1,1)
M2_end = end_i_offset + vector(0, 0.5 * drc["minwidth_metal2"])
2016-11-08 18:57:35 +01:00
if start_inv < half_length and end_inv >= half_length:
mid = [half_length * self.inv.width \
- 0.5 * drc["minwidth_metal2"], M2_start[1]]
2016-11-17 23:05:50 +01:00
self.add_wire(("metal2", "via2", "metal3"),
2016-11-08 18:57:35 +01:00
[M2_start, mid, M2_end])
else:
self.add_path(("metal2"), [M2_start, M2_end])
# set the start of next one after current end
start_inv = end_inv
def add_vddgnd_label(self):
"""add vdd and gnd labels"""
for i in range(3):
if i % 2:
self.add_label(text="vdd",
layer="metal1",
offset=[0, i * self.bitcell_height])
else:
self.add_label(text="gnd",
layer="metal1",
offset=[0, i * self.bitcell_height])