mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-08-29 09:30:38 +02:00
fix merge conflicts
This commit is contained in:
@@ -28,7 +28,6 @@ class bitcell_array(bitcell_base_array):
|
||||
# the replica bitcell in the control logic
|
||||
# self.offset_all_coordinates()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
""" Create and connect the netlist """
|
||||
self.add_modules()
|
||||
@@ -56,22 +55,21 @@ class bitcell_array(bitcell_base_array):
|
||||
for col in range(self.column_size):
|
||||
for row in range(self.row_size):
|
||||
name = "bit_r{0}_c{1}".format(row, col)
|
||||
self.cell_inst[row,col]=self.add_inst(name=name,
|
||||
mod=self.cell)
|
||||
self.cell_inst[row, col]=self.add_inst(name=name,
|
||||
mod=self.cell)
|
||||
self.connect_inst(self.get_bitcell_pins(col, row))
|
||||
|
||||
def analytical_power(self, corner, load):
|
||||
"""Power of Bitcell array and bitline in nW."""
|
||||
from tech import drc, parameter
|
||||
|
||||
# Dynamic Power from Bitline
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
bl_swing = OPTS.rbl_delay_percentage
|
||||
freq = spice["default_event_frequency"]
|
||||
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
||||
|
||||
# Calculate the bitcell power which currently only includes leakage
|
||||
# Calculate the bitcell power which currently only includes leakage
|
||||
cell_power = self.cell.analytical_power(corner, load)
|
||||
|
||||
# Leakage power grows with entire array and bitlines.
|
||||
@@ -85,7 +83,7 @@ class bitcell_array(bitcell_base_array):
|
||||
else:
|
||||
width = self.width
|
||||
wl_wire = self.generate_rc_net(int(self.column_size), width, drc("minwidth_m1"))
|
||||
wl_wire.wire_c = 2*spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
|
||||
wl_wire.wire_c = 2 * spice["min_tx_gate_c"] + wl_wire.wire_c # 2 access tx gate per cell
|
||||
return wl_wire
|
||||
|
||||
def gen_bl_wire(self):
|
||||
@@ -94,26 +92,26 @@ class bitcell_array(bitcell_base_array):
|
||||
else:
|
||||
height = self.height
|
||||
bl_pos = 0
|
||||
bl_wire = self.generate_rc_net(int(self.row_size-bl_pos), height, drc("minwidth_m1"))
|
||||
bl_wire = self.generate_rc_net(int(self.row_size - bl_pos), height, drc("minwidth_m1"))
|
||||
bl_wire.wire_c =spice["min_tx_drain_c"] + bl_wire.wire_c # 1 access tx d/s per cell
|
||||
return bl_wire
|
||||
|
||||
def get_wordline_cin(self):
|
||||
"""Get the relative input capacitance from the wordline connections in all the bitcell"""
|
||||
#A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||
# A single wordline is connected to all the bitcells in a single row meaning the capacitance depends on the # of columns
|
||||
bitcell_wl_cin = self.cell.get_wl_cin()
|
||||
total_cin = bitcell_wl_cin * self.column_size
|
||||
return total_cin
|
||||
|
||||
def graph_exclude_bits(self, targ_row, targ_col):
|
||||
"""Excludes bits in column from being added to graph except target"""
|
||||
#Function is not robust with column mux configurations
|
||||
# Function is not robust with column mux configurations
|
||||
for row in range(self.row_size):
|
||||
for col in range(self.column_size):
|
||||
if row == targ_row and col == targ_col:
|
||||
continue
|
||||
self.graph_inst_exclude.add(self.cell_inst[row,col])
|
||||
self.graph_inst_exclude.add(self.cell_inst[row, col])
|
||||
|
||||
def get_cell_name(self, inst_name, row, col):
|
||||
"""Gets the spice name of the target bitcell."""
|
||||
return inst_name+'.x'+self.cell_inst[row,col].name, self.cell_inst[row,col]
|
||||
"""Gets the spice name of the target bitcell."""
|
||||
return inst_name + '.x' + self.cell_inst[row, col].name, self.cell_inst[row, col]
|
||||
|
||||
@@ -90,7 +90,7 @@ class control_logic(design.design):
|
||||
self.add_mod(self.ctrl_dff_array)
|
||||
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
size=4,
|
||||
size=12,
|
||||
height=dff_height)
|
||||
self.add_mod(self.and2)
|
||||
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from tech import drc
|
||||
from contact import contact
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class delay_chain(design.design):
|
||||
"""
|
||||
Generate a delay chain with the given number of stages and fanout.
|
||||
@@ -28,7 +27,7 @@ class delay_chain(design.design):
|
||||
|
||||
# Two fanouts are needed so that we can route the vdd/gnd connections
|
||||
for f in fanout_list:
|
||||
debug.check(f>=2,"Must have >=2 fanouts for each stage.")
|
||||
debug.check(f>=2, "Must have >=2 fanouts for each stage.")
|
||||
|
||||
# number of inverters including any fanout loads.
|
||||
self.fanout_list = fanout_list
|
||||
@@ -36,7 +35,6 @@ class delay_chain(design.design):
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
self.create_layout()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_modules()
|
||||
@@ -45,12 +43,13 @@ class delay_chain(design.design):
|
||||
|
||||
def create_layout(self):
|
||||
# Each stage is a a row
|
||||
self.height = len(self.fanout_list)*self.inv.height
|
||||
self.height = len(self.fanout_list) * self.inv.height
|
||||
# The width is determined by the largest fanout plus the driver
|
||||
self.width = (max(self.fanout_list)+1) * self.inv.width
|
||||
self.width = (max(self.fanout_list) + 1) * self.inv.width
|
||||
|
||||
self.place_inverters()
|
||||
self.route_inverters()
|
||||
self.route_supplies()
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
@@ -69,9 +68,8 @@ class delay_chain(design.design):
|
||||
def create_inverters(self):
|
||||
""" Create the inverters and connect them based on the stage list """
|
||||
self.driver_inst_list = []
|
||||
self.rightest_load_inst = {}
|
||||
self.load_inst_map = {}
|
||||
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
|
||||
for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list):
|
||||
# Add the inverter
|
||||
cur_driver=self.add_inst(name="dinv{}".format(stage_num),
|
||||
mod=self.inv)
|
||||
@@ -79,40 +77,37 @@ class delay_chain(design.design):
|
||||
self.driver_inst_list.append(cur_driver)
|
||||
|
||||
# Hook up the driver
|
||||
if stage_num+1==len(self.fanout_list):
|
||||
if stage_num + 1 == len(self.fanout_list):
|
||||
stageout_name = "out"
|
||||
else:
|
||||
stageout_name = "dout_{}".format(stage_num+1)
|
||||
stageout_name = "dout_{}".format(stage_num + 1)
|
||||
if stage_num == 0:
|
||||
stagein_name = "in"
|
||||
else:
|
||||
stagein_name = "dout_{}".format(stage_num)
|
||||
stagein_name = "dout_{}".format(stage_num)
|
||||
self.connect_inst([stagein_name, stageout_name, "vdd", "gnd"])
|
||||
|
||||
# Now add the dummy loads to the right
|
||||
self.load_inst_map[cur_driver]=[]
|
||||
for i in range(fanout_size):
|
||||
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num,i),
|
||||
cur_load=self.add_inst(name="dload_{0}_{1}".format(stage_num, i),
|
||||
mod=self.inv)
|
||||
# Fanout stage is always driven by driver and output is disconnected
|
||||
disconnect_name = "n_{0}_{1}".format(stage_num,i)
|
||||
disconnect_name = "n_{0}_{1}".format(stage_num, i)
|
||||
self.connect_inst([stageout_name, disconnect_name, "vdd", "gnd"])
|
||||
|
||||
# Keep track of all the loads to connect their inputs as a load
|
||||
self.load_inst_map[cur_driver].append(cur_load)
|
||||
else:
|
||||
# Keep track of the last one so we can add the the wire later
|
||||
self.rightest_load_inst[cur_driver]=cur_load
|
||||
|
||||
def place_inverters(self):
|
||||
""" Place the inverters and connect them based on the stage list """
|
||||
for stage_num,fanout_size in zip(range(len(self.fanout_list)),self.fanout_list):
|
||||
for stage_num, fanout_size in zip(range(len(self.fanout_list)), self.fanout_list):
|
||||
if stage_num % 2:
|
||||
inv_mirror = "MX"
|
||||
inv_offset = vector(0, (stage_num+1)* self.inv.height)
|
||||
inv_offset = vector(0, (stage_num + 1) * self.inv.height)
|
||||
else:
|
||||
inv_mirror = "R0"
|
||||
inv_offset = vector(0, stage_num * self.inv.height)
|
||||
inv_offset = vector(0, stage_num * self.inv.height)
|
||||
|
||||
# Add the inverter
|
||||
cur_driver=self.driver_inst_list[stage_num]
|
||||
@@ -122,10 +117,9 @@ class delay_chain(design.design):
|
||||
# Now add the dummy loads to the right
|
||||
load_list = self.load_inst_map[cur_driver]
|
||||
for i in range(fanout_size):
|
||||
inv_offset += vector(self.inv.width,0)
|
||||
inv_offset += vector(self.inv.width, 0)
|
||||
load_list[i].place(offset=inv_offset,
|
||||
mirror=inv_mirror)
|
||||
|
||||
|
||||
def add_route(self, pin1, pin2):
|
||||
""" This guarantees that we route from the top to bottom row correctly. """
|
||||
@@ -134,9 +128,9 @@ class delay_chain(design.design):
|
||||
if pin1_pos.y == pin2_pos.y:
|
||||
self.add_path("m2", [pin1_pos, pin2_pos])
|
||||
else:
|
||||
mid_point = vector(pin2_pos.x, 0.5*(pin1_pos.y+pin2_pos.y))
|
||||
mid_point = vector(pin2_pos.x, 0.5 * (pin1_pos.y + pin2_pos.y))
|
||||
# Written this way to guarantee it goes right first if we are switching rows
|
||||
self.add_path("m2", [pin1_pos, vector(pin1_pos.x,mid_point.y), mid_point, vector(mid_point.x,pin2_pos.y), pin2_pos])
|
||||
self.add_path("m2", [pin1_pos, vector(pin1_pos.x, mid_point.y), mid_point, vector(mid_point.x, pin2_pos.y), pin2_pos])
|
||||
|
||||
def route_inverters(self):
|
||||
""" Add metal routing for each of the fanout stages """
|
||||
@@ -145,7 +139,7 @@ class delay_chain(design.design):
|
||||
inv = self.driver_inst_list[i]
|
||||
for load in self.load_inst_map[inv]:
|
||||
# Drop a via on each A pin
|
||||
a_pin = load.get_pin("A")
|
||||
a_pin = load.get_pin("A")
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=a_pin.center())
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
@@ -154,54 +148,42 @@ class delay_chain(design.design):
|
||||
# Route an M3 horizontal wire to the furthest
|
||||
z_pin = inv.get_pin("Z")
|
||||
a_pin = inv.get_pin("A")
|
||||
a_max = self.rightest_load_inst[inv].get_pin("A")
|
||||
a_max = self.load_inst_map[inv][-1].get_pin("A")
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=a_pin.center())
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=z_pin.center())
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=z_pin.center())
|
||||
self.add_path("m3",[z_pin.center(), a_max.center()])
|
||||
self.add_path("m3", [z_pin.center(), a_max.center()])
|
||||
|
||||
|
||||
# Route Z to the A of the next stage
|
||||
if i+1 < len(self.driver_inst_list):
|
||||
if i + 1 < len(self.driver_inst_list):
|
||||
z_pin = inv.get_pin("Z")
|
||||
next_inv = self.driver_inst_list[i+1]
|
||||
next_inv = self.driver_inst_list[i + 1]
|
||||
next_a_pin = next_inv.get_pin("A")
|
||||
y_mid = (z_pin.cy() + next_a_pin.cy())/2
|
||||
y_mid = (z_pin.cy() + next_a_pin.cy()) / 2
|
||||
mid1_point = vector(z_pin.cx(), y_mid)
|
||||
mid2_point = vector(next_a_pin.cx(), y_mid)
|
||||
self.add_path("m2",[z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
""" Add vdd and gnd rails and the input/output. Connect the gnd rails internally on
|
||||
the top end with no input/output to obstruct. """
|
||||
self.add_path("m2", [z_pin.center(), mid1_point, mid2_point, next_a_pin.center()])
|
||||
|
||||
def route_supplies(self):
|
||||
# Add power and ground to all the cells except:
|
||||
# the fanout driver, the right-most load
|
||||
# The routing to connect the loads is over the first and last cells
|
||||
# We have an even number of drivers and must only do every other
|
||||
# supply rail
|
||||
for i in range(0,len(self.driver_inst_list),2):
|
||||
inv = self.driver_inst_list[i]
|
||||
for load in self.load_inst_map[inv]:
|
||||
if load==self.rightest_load_inst[inv]:
|
||||
continue
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
pin = load.get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc())
|
||||
else:
|
||||
# We have an even number of rows, so need to get the last gnd rail
|
||||
inv = self.driver_inst_list[-1]
|
||||
for load in self.load_inst_map[inv]:
|
||||
if load==self.rightest_load_inst[inv]:
|
||||
continue
|
||||
pin_name = "gnd"
|
||||
pin = load.get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc())
|
||||
|
||||
for inst in self.driver_inst_list:
|
||||
load_list = self.load_inst_map[inst]
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
pin = load_list[0].get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc() - vector(self.m1_pitch, 0))
|
||||
|
||||
pin = load_list[-1].get_pin(pin_name)
|
||||
self.add_power_pin(pin_name, pin.rc() - vector(0.5 * self.m1_pitch, 0))
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
# input is A pin of first inverter
|
||||
a_pin = self.driver_inst_list[0].get_pin("A")
|
||||
@@ -209,36 +191,36 @@ class delay_chain(design.design):
|
||||
offset=a_pin.center())
|
||||
self.add_layout_pin(text="in",
|
||||
layer="m2",
|
||||
offset=a_pin.ll().scale(1,0),
|
||||
offset=a_pin.ll().scale(1, 0),
|
||||
height=a_pin.cy())
|
||||
|
||||
|
||||
# output is A pin of last load inverter
|
||||
last_driver_inst = self.driver_inst_list[-1]
|
||||
a_pin = self.rightest_load_inst[last_driver_inst].get_pin("A")
|
||||
a_pin = self.load_inst_map[last_driver_inst][-1].get_pin("A")
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=a_pin.center())
|
||||
mid_point = vector(a_pin.cx()+3*self.m2_width,a_pin.cy())
|
||||
self.add_path("m2",[a_pin.center(), mid_point, mid_point.scale(1,0)])
|
||||
mid_point = vector(a_pin.cx() + 3 * self.m2_width, a_pin.cy())
|
||||
self.add_path("m2", [a_pin.center(), mid_point, mid_point.scale(1, 0)])
|
||||
self.add_layout_pin_segment_center(text="out",
|
||||
layer="m2",
|
||||
start=mid_point,
|
||||
end=mid_point.scale(1,0))
|
||||
end=mid_point.scale(1, 0))
|
||||
|
||||
def get_cin(self):
|
||||
"""Get the enable input ralative capacitance"""
|
||||
#Only 1 input to the delay chain which is connected to an inverter.
|
||||
# Only 1 input to the delay chain which is connected to an inverter.
|
||||
dc_cin = self.inv.get_cin()
|
||||
return dc_cin
|
||||
return dc_cin
|
||||
|
||||
def determine_delayed_en_stage_efforts(self, ext_delayed_en_cout, inp_is_rise=True):
|
||||
"""Get the stage efforts from the en to s_en. Does not compute the delay for the bitline load."""
|
||||
stage_effort_list = []
|
||||
#Add a stage to the list for every stage in delay chain. Stages only differ in fanout except the last which has an external cout.
|
||||
# Add a stage to the list for every stage in delay chain.
|
||||
# Stages only differ in fanout except the last which has an external cout.
|
||||
last_stage_is_rise = inp_is_rise
|
||||
for stage_fanout in self.fanout_list:
|
||||
stage_cout = self.inv.get_cin()*(stage_fanout+1)
|
||||
if len(stage_effort_list) == len(self.fanout_list)-1: #last stage
|
||||
stage_cout = self.inv.get_cin() * (stage_fanout + 1)
|
||||
if len(stage_effort_list) == len(self.fanout_list) - 1:
|
||||
stage_cout+=ext_delayed_en_cout
|
||||
stage = self.inv.get_stage_effort(stage_cout, last_stage_is_rise)
|
||||
stage_effort_list.append(stage)
|
||||
|
||||
+16
-17
@@ -35,7 +35,7 @@ class dff_buf(design.design):
|
||||
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
|
||||
# contact does not violate spacing to the rail in the NMOS.
|
||||
debug.check(inv1_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
|
||||
debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
|
||||
debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
|
||||
|
||||
self.inv1_size=inv1_size
|
||||
self.inv2_size=inv2_size
|
||||
@@ -52,14 +52,13 @@ class dff_buf(design.design):
|
||||
def create_layout(self):
|
||||
self.place_instances()
|
||||
self.width = self.inv2_inst.rx()
|
||||
|
||||
self.height = self.dff.height
|
||||
|
||||
|
||||
self.route_wires()
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
|
||||
def add_modules(self):
|
||||
self.dff = factory.create(module_type="dff")
|
||||
self.add_mod(self.dff)
|
||||
@@ -73,8 +72,6 @@ class dff_buf(design.design):
|
||||
size=self.inv2_size,
|
||||
height=self.dff.height)
|
||||
self.add_mod(self.inv2)
|
||||
|
||||
|
||||
|
||||
def add_pins(self):
|
||||
self.add_pin("D", "INPUT")
|
||||
@@ -96,17 +93,19 @@ class dff_buf(design.design):
|
||||
|
||||
self.inv1_inst=self.add_inst(name="dff_buf_inv1",
|
||||
mod=self.inv1)
|
||||
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
|
||||
self.connect_inst(["qint", "Qb", "vdd", "gnd"])
|
||||
|
||||
self.inv2_inst=self.add_inst(name="dff_buf_inv2",
|
||||
mod=self.inv2)
|
||||
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
||||
self.connect_inst(["Qb", "Q", "vdd", "gnd"])
|
||||
|
||||
def place_instances(self):
|
||||
# Add the DFF
|
||||
self.dff_inst.place(vector(0,0))
|
||||
self.dff_inst.place(vector(0, 0))
|
||||
|
||||
# Add INV1 to the right
|
||||
# The INV needs well spacing because the DFF is likely from a library
|
||||
# with different well construction rules
|
||||
well_spacing = 0
|
||||
try:
|
||||
well_spacing = max(well_spacing, self.nwell_space)
|
||||
@@ -129,7 +128,7 @@ class dff_buf(design.design):
|
||||
# Route dff q to inv1 a
|
||||
q_pin = self.dff_inst.get_pin("Q")
|
||||
a1_pin = self.inv1_inst.get_pin("A")
|
||||
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
|
||||
mid_x_offset = 0.5 * (a1_pin.cx() + q_pin.cx())
|
||||
mid1 = vector(mid_x_offset, q_pin.cy())
|
||||
mid2 = vector(mid_x_offset, a1_pin.cy())
|
||||
self.add_path("m3", [q_pin.center(), mid1, mid2, a1_pin.center()])
|
||||
@@ -143,7 +142,7 @@ class dff_buf(design.design):
|
||||
# Route inv1 z to inv2 a
|
||||
z1_pin = self.inv1_inst.get_pin("Z")
|
||||
a2_pin = self.inv2_inst.get_pin("A")
|
||||
mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx())
|
||||
mid_x_offset = 0.5 * (z1_pin.cx() + a2_pin.cx())
|
||||
self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy())
|
||||
mid2 = vector(mid_x_offset, a2_pin.cy())
|
||||
self.add_path("m1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()])
|
||||
@@ -181,8 +180,8 @@ class dff_buf(design.design):
|
||||
height=din_pin.height())
|
||||
|
||||
dout_pin = self.inv2_inst.get_pin("Z")
|
||||
mid_pos = dout_pin.center() + vector(self.m1_pitch,0)
|
||||
q_pos = mid_pos - vector(0,self.m2_pitch)
|
||||
mid_pos = dout_pin.center() + vector(self.m1_pitch, 0)
|
||||
q_pos = mid_pos - vector(0, self.m2_pitch)
|
||||
self.add_layout_pin_rect_center(text="Q",
|
||||
layer="m2",
|
||||
offset=q_pos)
|
||||
@@ -190,7 +189,7 @@ class dff_buf(design.design):
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=q_pos)
|
||||
|
||||
qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch)
|
||||
qb_pos = self.mid_qb_pos + vector(0, self.m2_pitch)
|
||||
self.add_layout_pin_rect_center(text="Qb",
|
||||
layer="m2",
|
||||
offset=qb_pos)
|
||||
@@ -200,7 +199,7 @@ class dff_buf(design.design):
|
||||
|
||||
def get_clk_cin(self):
|
||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff"""
|
||||
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
||||
#Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
|
||||
#FIXME: Dff changed in a past commit. The parameter need to be updated.
|
||||
# This is a handmade cell so the value must be entered in the tech.py file or estimated.
|
||||
# Calculated in the tech file by summing the widths of all the gates and dividing by the minimum width.
|
||||
# FIXME: Dff changed in a past commit. The parameter need to be updated.
|
||||
return parameter["dff_clk_cin"]
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
from tech import drc
|
||||
from tech import cell_properties as props
|
||||
from math import log
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from sram_factory import factory
|
||||
|
||||
|
||||
class dff_buf_array(design.design):
|
||||
"""
|
||||
This is a simple row (or multiple rows) of flops.
|
||||
@@ -49,18 +48,19 @@ class dff_buf_array(design.design):
|
||||
self.width = self.columns * self.dff.width
|
||||
self.height = self.rows * self.dff.height
|
||||
self.place_dff_array()
|
||||
self.route_supplies()
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for row in range(self.rows):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_din_name(row,col), "INPUT")
|
||||
for row in range(self.rows):
|
||||
self.add_pin(self.get_din_name(row, col), "INPUT")
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
self.add_pin(self.get_dout_name(row,col), "OUTPUT")
|
||||
self.add_pin(self.get_dout_bar_name(row,col), "OUTPUT")
|
||||
self.add_pin(self.get_dout_name(row, col), "OUTPUT")
|
||||
self.add_pin(self.get_dout_bar_name(row, col), "OUTPUT")
|
||||
self.add_pin("clk", "INPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
@@ -75,17 +75,16 @@ class dff_buf_array(design.design):
|
||||
inv2_size=self.inv2_size)
|
||||
self.add_mod(self.dff)
|
||||
|
||||
|
||||
def create_dff_array(self):
|
||||
self.dff_insts={}
|
||||
for row in range(self.rows):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "dff_r{0}_c{1}".format(row,col)
|
||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||
mod=self.dff)
|
||||
inst_ports = [self.get_din_name(row,col),
|
||||
self.get_dout_name(row,col),
|
||||
self.get_dout_bar_name(row,col),
|
||||
name = "dff_r{0}_c{1}".format(row, col)
|
||||
self.dff_insts[row, col]=self.add_inst(name=name,
|
||||
mod=self.dff)
|
||||
inst_ports = [self.get_din_name(row, col),
|
||||
self.get_dout_name(row, col),
|
||||
self.get_dout_bar_name(row, col),
|
||||
"clk",
|
||||
"vdd",
|
||||
"gnd"]
|
||||
@@ -96,23 +95,33 @@ class dff_buf_array(design.design):
|
||||
|
||||
def place_dff_array(self):
|
||||
|
||||
well_spacing = max(self.nwell_space,
|
||||
self.pwell_space,
|
||||
self.pwell_to_nwell)
|
||||
well_spacing = 0
|
||||
try:
|
||||
well_spacing = max(self.nwell_space, well_spacing)
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
well_spacing = max(self.pwell_space, well_spacing)
|
||||
except AttributeError:
|
||||
pass
|
||||
try:
|
||||
well_spacing = max(self.pwell_to_nwell, well_spacing)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
dff_pitch = self.dff.width + well_spacing + self.well_extend_active
|
||||
|
||||
for row in range(self.rows):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
||||
# name = "Xdff_r{0}_c{1}".format(row, col)
|
||||
if (row % 2 == 0):
|
||||
base = vector(col*dff_pitch,row*self.dff.height)
|
||||
base = vector(col * dff_pitch, row * self.dff.height)
|
||||
mirror = "R0"
|
||||
else:
|
||||
base = vector(col*dff_pitch,(row+1)*self.dff.height)
|
||||
base = vector(col * dff_pitch, (row + 1) * self.dff.height)
|
||||
mirror = "MX"
|
||||
self.dff_insts[row,col].place(offset=base,
|
||||
mirror=mirror)
|
||||
self.dff_insts[row, col].place(offset=base,
|
||||
mirror=mirror)
|
||||
|
||||
def get_din_name(self, row, col):
|
||||
if self.columns == 1:
|
||||
@@ -120,7 +129,7 @@ class dff_buf_array(design.design):
|
||||
elif self.rows == 1:
|
||||
din_name = "din_{0}".format(col)
|
||||
else:
|
||||
din_name = "din_{0}_{1}".format(row,col)
|
||||
din_name = "din_{0}_{1}".format(row, col)
|
||||
|
||||
return din_name
|
||||
|
||||
@@ -130,7 +139,7 @@ class dff_buf_array(design.design):
|
||||
elif self.rows == 1:
|
||||
dout_name = "dout_{0}".format(col)
|
||||
else:
|
||||
dout_name = "dout_{0}_{1}".format(row,col)
|
||||
dout_name = "dout_{0}_{1}".format(row, col)
|
||||
|
||||
return dout_name
|
||||
|
||||
@@ -140,75 +149,84 @@ class dff_buf_array(design.design):
|
||||
elif self.rows == 1:
|
||||
dout_bar_name = "dout_bar_{0}".format(col)
|
||||
else:
|
||||
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
|
||||
dout_bar_name = "dout_bar_{0}_{1}".format(row, col)
|
||||
|
||||
return dout_bar_name
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
def route_supplies(self):
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
vdd0_pin=self.dff_insts[row, 0].get_pin("vdd")
|
||||
vddn_pin=self.dff_insts[row, self.columns - 1].get_pin("vdd")
|
||||
self.add_path(vdd0_pin.layer, [vdd0_pin.lc(), vddn_pin.rc()], width=vdd0_pin.height())
|
||||
|
||||
gnd0_pin=self.dff_insts[row, 0].get_pin("gnd")
|
||||
gndn_pin=self.dff_insts[row, self.columns - 1].get_pin("gnd")
|
||||
self.add_path(gnd0_pin.layer, [gnd0_pin.lc(), gndn_pin.rc()], width=gnd0_pin.height())
|
||||
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
# Continous vdd rail along with label.
|
||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
||||
vdd_pin=self.dff_insts[row, col].get_pin("vdd")
|
||||
self.add_power_pin("vdd", vdd_pin.lc())
|
||||
|
||||
# Continous gnd rail along with label.
|
||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
||||
gnd_pin=self.dff_insts[row, col].get_pin("gnd")
|
||||
self.add_power_pin("gnd", gnd_pin.lc())
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
din_pin = self.dff_insts[row,col].get_pin("D")
|
||||
debug.check(din_pin.layer=="m2","DFF D pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_din_name(row,col),
|
||||
for row in range(self.rows):
|
||||
for col in range(self.columns):
|
||||
din_pin = self.dff_insts[row, col].get_pin("D")
|
||||
debug.check(din_pin.layer=="m2", "DFF D pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_din_name(row, col),
|
||||
layer=din_pin.layer,
|
||||
offset=din_pin.ll(),
|
||||
width=din_pin.width(),
|
||||
height=din_pin.height())
|
||||
|
||||
dout_pin = self.dff_insts[row,col].get_pin("Q")
|
||||
debug.check(dout_pin.layer=="m2","DFF Q pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_name(row,col),
|
||||
dout_pin = self.dff_insts[row, col].get_pin("Q")
|
||||
debug.check(dout_pin.layer=="m2", "DFF Q pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_name(row, col),
|
||||
layer=dout_pin.layer,
|
||||
offset=dout_pin.ll(),
|
||||
width=dout_pin.width(),
|
||||
height=dout_pin.height())
|
||||
|
||||
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
|
||||
debug.check(dout_bar_pin.layer=="m2","DFF Qb pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
|
||||
dout_bar_pin = self.dff_insts[row, col].get_pin("Qb")
|
||||
debug.check(dout_bar_pin.layer=="m2", "DFF Qb pin not on metal2")
|
||||
self.add_layout_pin(text=self.get_dout_bar_name(row, col),
|
||||
layer=dout_bar_pin.layer,
|
||||
offset=dout_bar_pin.ll(),
|
||||
width=dout_bar_pin.width(),
|
||||
height=dout_bar_pin.height())
|
||||
|
||||
|
||||
# Create vertical spines to a single horizontal rail
|
||||
clk_pin = self.dff_insts[0,0].get_pin("clk")
|
||||
clk_ypos = 2*self.m3_pitch+self.m3_width
|
||||
debug.check(clk_pin.layer=="m2","DFF clk pin not on metal2")
|
||||
clk_pin = self.dff_insts[0, 0].get_pin("clk")
|
||||
clk_ypos = 2 * self.m3_pitch + self.m3_width
|
||||
debug.check(clk_pin.layer=="m2", "DFF clk pin not on metal2")
|
||||
if self.columns==1:
|
||||
self.add_layout_pin(text="clk",
|
||||
layer="m2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
offset=clk_pin.ll().scale(1, 0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
else:
|
||||
self.add_layout_pin_segment_center(text="clk",
|
||||
layer="m3",
|
||||
start=vector(0,clk_ypos),
|
||||
end=vector(self.width,clk_ypos))
|
||||
layer="m3",
|
||||
start=vector(0, clk_ypos),
|
||||
end=vector(self.width, clk_ypos))
|
||||
for col in range(self.columns):
|
||||
clk_pin = self.dff_insts[0,col].get_pin("clk")
|
||||
clk_pin = self.dff_insts[0, col].get_pin("clk")
|
||||
|
||||
# Make a vertical strip for each column
|
||||
self.add_rect(layer="m2",
|
||||
offset=clk_pin.ll().scale(1,0),
|
||||
offset=clk_pin.ll().scale(1, 0),
|
||||
width=self.m2_width,
|
||||
height=self.height)
|
||||
# Drop a via to the M3 pin
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=vector(clk_pin.cx(),clk_ypos))
|
||||
offset=vector(clk_pin.cx(), clk_ypos))
|
||||
|
||||
def get_clk_cin(self):
|
||||
"""Return the total capacitance (in relative units) that the clock is loaded by in the dff array"""
|
||||
|
||||
@@ -32,7 +32,6 @@ class hierarchical_decoder(design.design):
|
||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||
except AttributeError:
|
||||
self.cell_multiple = 1
|
||||
# For debugging
|
||||
self.cell_height = self.cell_multiple * b.height
|
||||
|
||||
self.num_outputs = num_outputs
|
||||
|
||||
@@ -699,8 +699,13 @@ class port_data(design.design):
|
||||
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
||||
top_names = self._get_bitline_pins(top_inst_group, bit)
|
||||
|
||||
if bottom_names[0].layer == "m2":
|
||||
bitline_dirs = ("H", "V")
|
||||
elif bottom_names[0].layer == "m1":
|
||||
bitline_dirs = ("V", "H")
|
||||
|
||||
route_map = list(zip(bottom_names, top_names))
|
||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
|
||||
self.create_horizontal_channel_route(route_map, offset, self.m1_stack, bitline_dirs)
|
||||
|
||||
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||
inst1_bls_template="{inst}_{bit}",
|
||||
|
||||
@@ -151,7 +151,7 @@ class single_level_column_mux_array(design.design):
|
||||
def add_horizontal_input_rail(self):
|
||||
""" Create address input rails on M1 below the mux transistors """
|
||||
for j in range(self.words_per_row):
|
||||
offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch) #edit
|
||||
offset = vector(0, self.route_height + (j - self.words_per_row) * self.col_mux_stack_pitch)
|
||||
self.add_layout_pin(text="sel_{}".format(j),
|
||||
layer=self.col_mux_stack[0],
|
||||
offset=offset,
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
#
|
||||
import debug
|
||||
import design
|
||||
import math
|
||||
import contact
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
from tech import cell_properties
|
||||
|
||||
|
||||
class wordline_driver(design.design):
|
||||
@@ -26,6 +28,13 @@ class wordline_driver(design.design):
|
||||
|
||||
self.rows = rows
|
||||
self.cols = cols
|
||||
|
||||
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.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
@@ -37,6 +46,7 @@ class wordline_driver(design.design):
|
||||
self.create_drivers()
|
||||
|
||||
def create_layout(self):
|
||||
self.setup_layout_constants()
|
||||
self.place_drivers()
|
||||
self.route_layout()
|
||||
self.route_vdd_gnd()
|
||||
@@ -56,17 +66,10 @@ class wordline_driver(design.design):
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
b = factory.create(module_type="bitcell")
|
||||
|
||||
self.inv = factory.create(module_type="pdriver",
|
||||
fanout=self.cols,
|
||||
neg_polarity=True,
|
||||
height=b.height)
|
||||
self.add_mod(self.inv)
|
||||
|
||||
self.nand2 = factory.create(module_type="pnand2",
|
||||
height=b.height)
|
||||
self.add_mod(self.nand2)
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
height=self.cell_height,
|
||||
size=self.cols)
|
||||
self.add_mod(self.and2)
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
"""
|
||||
@@ -75,68 +78,64 @@ class wordline_driver(design.design):
|
||||
"""
|
||||
|
||||
# Find the x offsets for where the vias/pins should be placed
|
||||
a_xoffset = self.nand_inst[0].lx()
|
||||
xoffset_list = [self.and_inst[0].lx()]
|
||||
for num in range(self.rows):
|
||||
# this will result in duplicate polygons for rails, but who cares
|
||||
|
||||
# use the inverter offset even though it will be the nand's too
|
||||
# use the inverter offset even though it will be the and's too
|
||||
(gate_offset, y_dir) = self.get_gate_offset(0,
|
||||
self.inv.height,
|
||||
self.and2.height,
|
||||
num)
|
||||
|
||||
# Route both supplies
|
||||
for name in ["vdd", "gnd"]:
|
||||
supply_pin = self.inv2_inst[num].get_pin(name)
|
||||
supply_pin = self.and_inst[num].get_pin(name)
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [a_xoffset]:
|
||||
for xoffset in xoffset_list:
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_power_pin(name, pin_pos)
|
||||
|
||||
def create_drivers(self):
|
||||
self.nand_inst = []
|
||||
self.inv2_inst = []
|
||||
self.and_inst = []
|
||||
for row in range(self.rows):
|
||||
name_nand = "wl_driver_nand{}".format(row)
|
||||
name_inv2 = "wl_driver_inv{}".format(row)
|
||||
name_and = "wl_driver_and{}".format(row)
|
||||
|
||||
# add nand 2
|
||||
self.nand_inst.append(self.add_inst(name=name_nand,
|
||||
mod=self.nand2))
|
||||
# add and2
|
||||
self.and_inst.append(self.add_inst(name=name_and,
|
||||
mod=self.and2))
|
||||
self.connect_inst(["en",
|
||||
"in_{0}".format(row),
|
||||
"wl_bar_{0}".format(row),
|
||||
"vdd", "gnd"])
|
||||
# add inv2
|
||||
self.inv2_inst.append(self.add_inst(name=name_inv2,
|
||||
mod=self.inv))
|
||||
self.connect_inst(["wl_bar_{0}".format(row),
|
||||
"wl_{0}".format(row),
|
||||
"vdd", "gnd"])
|
||||
|
||||
def setup_layout_constants(self):
|
||||
# We may have more than one bitcell per decoder row
|
||||
self.num_rows = math.ceil(self.rows / self.cell_multiple)
|
||||
# We will place this many final decoders per row
|
||||
self.decoders_per_row = math.ceil(self.rows / self.num_rows)
|
||||
|
||||
def place_drivers(self):
|
||||
nand2_xoffset = 2 * self.m1_width + 5 * self.m1_space
|
||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||
and2_xoffset = 2 * self.m1_width + 5 * self.m1_space
|
||||
|
||||
self.width = inv2_xoffset + self.inv.width
|
||||
self.height = self.inv.height * self.rows
|
||||
self.width = and2_xoffset + self.and2.width
|
||||
self.height = self.and2.height * self.num_rows
|
||||
|
||||
for row in range(self.rows):
|
||||
#row = math.floor(inst_index / self.decoders_per_row)
|
||||
#dec = inst_index % self.decoders_per_row
|
||||
|
||||
if (row % 2):
|
||||
y_offset = self.inv.height * (row + 1)
|
||||
y_offset = self.and2.height * (row + 1)
|
||||
inst_mirror = "MX"
|
||||
else:
|
||||
y_offset = self.inv.height * row
|
||||
y_offset = self.and2.height * row
|
||||
inst_mirror = "R0"
|
||||
|
||||
nand2_offset = [nand2_xoffset, y_offset]
|
||||
inv2_offset = [inv2_xoffset, y_offset]
|
||||
# x_off = self.internal_routing_width + dec * and_mod.width
|
||||
and2_offset = [and2_xoffset, y_offset]
|
||||
|
||||
# add nand 2
|
||||
self.nand_inst[row].place(offset=nand2_offset,
|
||||
mirror=inst_mirror)
|
||||
# add inv2
|
||||
self.inv2_inst[row].place(offset=inv2_offset,
|
||||
# add and2
|
||||
self.and_inst[row].place(offset=and2_offset,
|
||||
mirror=inst_mirror)
|
||||
|
||||
def route_layout(self):
|
||||
@@ -151,11 +150,10 @@ class wordline_driver(design.design):
|
||||
height=self.height)
|
||||
|
||||
for row in range(self.rows):
|
||||
nand_inst = self.nand_inst[row]
|
||||
inv2_inst = self.inv2_inst[row]
|
||||
and_inst = self.and_inst[row]
|
||||
|
||||
# en connection
|
||||
a_pin = nand_inst.get_pin("A")
|
||||
a_pin = and_inst.get_pin("A")
|
||||
a_pos = a_pin.lc()
|
||||
clk_offset = vector(en_pin.bc().x, a_pos.y)
|
||||
self.add_segment_center(layer="m1",
|
||||
@@ -164,18 +162,10 @@ class wordline_driver(design.design):
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=clk_offset)
|
||||
|
||||
# Nand2 out to 2nd inv
|
||||
zr_pos = nand_inst.get_pin("Z").rc()
|
||||
al_pos = inv2_inst.get_pin("A").lc()
|
||||
# ensure the bend is in the middle
|
||||
mid1_pos = vector(0.5 * (zr_pos.x + al_pos.x), zr_pos.y)
|
||||
mid2_pos = vector(0.5 * (zr_pos.x + al_pos.x), al_pos.y)
|
||||
self.add_path("m1", [zr_pos, mid1_pos, mid2_pos, al_pos])
|
||||
|
||||
# connect the decoder input pin to nand2 B
|
||||
b_pin = nand_inst.get_pin("B")
|
||||
# connect the decoder input pin to and2 B
|
||||
b_pin = and_inst.get_pin("B")
|
||||
b_pos = b_pin.lc()
|
||||
# needs to move down since B nand input is
|
||||
# needs to move down since B and input is
|
||||
# nearly aligned with A inv input
|
||||
up_or_down = self.m2_space if row % 2 else -self.m2_space
|
||||
input_offset = vector(0, b_pos.y + up_or_down)
|
||||
@@ -192,7 +182,7 @@ class wordline_driver(design.design):
|
||||
offset=mid_via_offset,
|
||||
directions=("V", "V"))
|
||||
|
||||
# now connect to the nand2 B
|
||||
# now connect to the and2 B
|
||||
self.add_path("m2", [mid_via_offset, b_pos])
|
||||
contact_offset = b_pos - vector(0.5 * contact.m1_via.height, 0)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
@@ -200,7 +190,7 @@ class wordline_driver(design.design):
|
||||
directions=("H", "H"))
|
||||
|
||||
# output each WL on the right
|
||||
wl_offset = inv2_inst.get_pin("Z").rc()
|
||||
wl_offset = and_inst.get_pin("Z").rc()
|
||||
self.add_layout_pin_segment_center(text="wl_{0}".format(row),
|
||||
layer="m1",
|
||||
start=wl_offset,
|
||||
@@ -213,13 +203,8 @@ class wordline_driver(design.design):
|
||||
"""
|
||||
stage_effort_list = []
|
||||
|
||||
stage1_cout = self.inv.get_cin()
|
||||
stage1 = self.nand2.get_stage_effort(stage1_cout, inp_is_rise)
|
||||
stage1 = self.and2.get_stage_effort(external_cout, inp_is_rise)
|
||||
stage_effort_list.append(stage1)
|
||||
last_stage_is_rise = stage1.is_rise
|
||||
|
||||
stage2 = self.inv.get_stage_efforts(external_cout, last_stage_is_rise)
|
||||
stage_effort_list.extend(stage2)
|
||||
|
||||
return stage_effort_list
|
||||
|
||||
@@ -228,6 +213,6 @@ class wordline_driver(design.design):
|
||||
Get the relative capacitance of all
|
||||
the enable connections in the bank
|
||||
"""
|
||||
# The enable is connected to a nand2 for every row.
|
||||
total_cin = self.nand2.get_cin() * self.rows
|
||||
# The enable is connected to a and2 for every row.
|
||||
total_cin = self.and2.get_cin() * self.rows
|
||||
return total_cin
|
||||
|
||||
Reference in New Issue
Block a user