Two bank SRAMs working in both technologies.

This commit is contained in:
Matt Guthaus 2017-09-29 16:22:13 -07:00
parent d29dd03373
commit e06e1691c8
41 changed files with 20945 additions and 19280 deletions

View File

@ -39,7 +39,7 @@ class bank(design.design):
self.num_banks = num_banks
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the signals.
# so this prefix will be added to all of the input signals.
if self.num_banks>1:
self.prefix="gated_"
else:
@ -58,6 +58,8 @@ class bank(design.design):
# Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
self.DRC_LVS()
@ -71,9 +73,9 @@ class bank(design.design):
# For more than one bank, we have a bank select and name
# the signals gated_*.
if(self.num_banks > 1):
self.add_pin("bank_select")
self.add_pin("bank_sel")
for pin in ["s_en","w_en","tri_en_bar","tri_en",
"clk_bar","clk","vdd","gnd"]:
"clk_bar","clk_buf","vdd","gnd"]:
self.add_pin(pin)
def route_layout(self):
@ -91,7 +93,7 @@ class bank(design.design):
self.route_vdd_supply()
self.route_gnd_supply()
self.offset_all_coordinates()
def add_modules(self):
@ -135,7 +137,7 @@ class bank(design.design):
# Number of control lines in the bus
self.num_control_lines = 6
# The order of the control signals on the control bus:
self.input_control_signals = ["clk", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"]
self.input_control_signals = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"]
# These will be outputs of the gaters if this is multibank
if self.num_banks>1:
self.control_signals = ["gated_"+str for str in self.input_control_signals]
@ -158,8 +160,8 @@ class bank(design.design):
# Overall central bus gap. It includes all the column mux lines,
# control lines, address flop to decoder lines and a GND power rail in M2
# one pitch on the right on the right of the control lines
self.start_of_right_central_bus = -self.m2_pitch * (self.num_control_lines + 1)
# 1.5 pitches on the right on the right of the control lines for vias (e.g. column mux addr lines)
self.start_of_right_central_bus = -self.m2_pitch * (self.num_control_lines + 1.5)
# one pitch on the right on the addr lines and one on the right of the gnd rail
self.start_of_left_central_bus = self.start_of_right_central_bus - self.m2_pitch*(self.num_addr_lines+2) - self.gnd_rail_width
# add a pitch on each end and around the gnd rail
@ -397,7 +399,7 @@ class bank(design.design):
temp.append("dec_out[{0}]".format(i))
for i in range(self.num_rows):
temp.append("wl[{0}]".format(i))
temp.append(self.prefix+"clk")
temp.append(self.prefix+"clk_buf")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
@ -426,7 +428,7 @@ class bank(design.design):
temp.extend(["sel[1]","sel[0]"])
else:
temp.extend(["A[{0}]".format(i),"A_bar[{0}]".format(i)])
temp.append(self.prefix+"clk")
temp.append(self.prefix+"clk_buf")
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
@ -482,9 +484,9 @@ class bank(design.design):
self.add_mod(self.nand2)
# left of gnd rail is the "bus start"
bus_start = self.gnd_x_offset - 2*drc["minwidth_metal2"]
xoffset_nand = bus_start - self.nand2.width - self.inv4x.width - 2*self.m2_pitch
xoffset_nor = bus_start - self.nor2.width - self.inv4x.width - 2*self.m2_pitch
bus_start = self.gnd_x_offset - drc["metal2_to_metal2"]
xoffset_nand = bus_start - self.nand2.width - self.inv4x.width - drc["pwell_to_nwell"]
xoffset_nor = bus_start - self.nor2.width - self.inv4x.width - drc["pwell_to_nwell"]
xoffset_inv = bus_start - self.inv4x.width
xoffset_bank_sel_inv = xoffset_nor - self.inv.width - 3*self.m2_pitch
xoffset_inputs = xoffset_bank_sel_inv - 6*self.m2_pitch
@ -500,32 +502,38 @@ class bank(design.design):
# bank_sel is vertical wire
xoffset_bank_sel = xoffset_bank_sel_inv
bank_sel_line_pos = vector(xoffset_bank_sel, self.min_point)
self.add_label_pin(text="bank_sel",
layer="metal2",
offset=vector(xoffset_bank_sel, self.min_point),
offset=bank_sel_line_pos,
width=self.m2_width,
height=self.decoder_min_point-self.min_point-self.m2_pitch)
in_pin = bank_sel_inv.get_pin("A").ll()
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin)
self.add_layout_pin(text="bank_sel",
layer="metal3",
offset=vector(self.left_vdd_x_offset, self.min_point),
width=xoffset_bank_sel - self.left_vdd_x_offset,
height=self.m3_width)
self.add_via(layers=("metal2","via2","metal3"),
offset=vector(xoffset_bank_sel_inv, self.min_point))
bank_sel_inv_in_pos = bank_sel_inv.get_pin("A").lc()
self.add_center_via(layers=("metal1","via1","metal2"),
offset=bank_sel_inv_in_pos,
rotate=90)
bank_sel_line_pos = vector(xoffset_bank_sel + 0.5*self.m2_width, self.min_point)
bank_sel_pin_pos=vector(self.left_vdd_x_offset, self.min_point)
self.add_center_layout_pin(text="bank_sel",
layer="metal3",
start=bank_sel_pin_pos,
end=bank_sel_line_pos)
self.add_center_via(layers=("metal2","via2","metal3"),
offset=bank_sel_line_pos,
rotate=90)
# bank_sel_bar is vertical wire
xoffset_bank_sel_bar = xoffset_bank_sel_inv+self.inv.width-self.m2_width
xoffset_bank_sel_bar = bank_sel_inv.get_pin("Z").rx()
self.add_label_pin(text="bank_sel_bar",
layer="metal2",
offset=vector(xoffset_bank_sel_bar, self.min_point),
width=self.m2_width,
height=2*self.inv.height)
out_pin = bank_sel_inv.get_pin("Z").lr()
self.add_via(layers=("metal1","via1","metal2"),
offset=out_pin-vector(self.m2_width,0))
bank_sel_out_pin = bank_sel_inv.get_pin("Z").rc()
self.add_center_via(layers=("metal1","via1","metal2"),
offset=bank_sel_out_pin)
@ -545,7 +553,7 @@ class bank(design.design):
# These require OR (nor2+inv) gates since they are active low.
# (writes occur on clk low)
if input_name in ("clk", "tri_en_bar"):
if input_name in ("clk_buf", "tri_en_bar"):
logic_inst=self.add_inst(name=name_nor,
mod=self.nor2,
@ -592,43 +600,43 @@ class bank(design.design):
self.add_path("metal1", [pre, out_position, in_position, post])
# Connect the inverter output to the central bus
out_pin = inv_inst.get_pin("Z")
bus_pos = vector(self.central_line_xoffset[gated_name], out_pin.rc().y)
self.add_path("metal3",[out_pin.rc(), bus_pos])
self.add_via(layers=("metal2", "via2", "metal3"),
offset=bus_pos - vector(0,0.5*self.m2m3_via.height))
self.add_via(layers=("metal1", "via1", "metal2"),
offset=out_pin.lr() - self.via_shift_offset,
rotate=90)
self.add_via(layers=("metal2", "via2", "metal3"),
offset=out_pin.lr() - self.via_shift_offset,
rotate=90)
out_pos = inv_inst.get_pin("Z").rc() - vector(0.5*self.m1m2_via.height,0)
bus_pos = vector(self.central_line_xoffset[gated_name] + 0.5*self.m2_width, out_pos.y)
self.add_path("metal3",[out_pos, bus_pos])
self.add_center_via(layers=("metal2", "via2", "metal3"),
offset=bus_pos,
rotate=90)
self.add_center_via(layers=("metal1", "via1", "metal2"),
offset=out_pos,
rotate=90)
self.add_center_via(layers=("metal2", "via2", "metal3"),
offset=out_pos,
rotate=90)
# Connect the logic B input to bank_sel/bank_sel_bar
logic_pin = logic_inst.get_pin("B")
input_pos = vector(xoffset_bank_signal,logic_pin.cy())
self.add_path("metal2",[logic_pin.lc(), input_pos])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=logic_pin.ll()-self.via_shift_offset,
rotate=90)
self.add_center_via(layers=("metal1", "via1", "metal2"),
offset=logic_pin.lc(),
rotate=90)
# Connect the logic A input to the input pin
logic_pos = logic_inst.get_pin("A").ll()
logic_pos = logic_inst.get_pin("A").lc()
input_pos = vector(self.left_vdd_x_offset,logic_pos.y)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=logic_pos-self.via_shift_offset,
rotate=90)
self.add_center_via(layers=("metal1", "via1", "metal2"),
offset=logic_pos,
rotate=90)
self.add_via(layers=("metal2", "via2", "metal3"),
offset=logic_pos-self.via_shift_offset,
rotate=90)
self.add_center_via(layers=("metal2", "via2", "metal3"),
offset=logic_pos,
rotate=90)
self.add_layout_pin(text=input_name,
layer="metal3",
offset=vector(self.left_vdd_x_offset,logic_pos.y - self.via_shift_offset.y),
height=self.m3_width,
width=logic_pos.x-self.left_vdd_x_offset)
self.add_center_layout_pin(text=input_name,
layer="metal3",
start=input_pos,
end=logic_pos)
@ -647,6 +655,7 @@ class bank(design.design):
self.add_path("metal1",[left_vdd_pos,vdd_pin.rc()])
def setup_layout_constraints(self):
""" Calculating layout constraints, width, height etc """
@ -665,7 +674,7 @@ class bank(design.design):
if self.num_banks>1:
self.min_point = min(self.decoder_min_point - self.num_control_lines * self.bitcell.height, tri_gate_min_point)
else:
self.min_point = min(addr_min_point, tri_gate_min_point)
self.min_point = min(self.decoder_min_point, addr_min_point, tri_gate_min_point)
# The max point is always the top of the precharge bitlines
@ -722,7 +731,7 @@ class bank(design.design):
# row address lines (to the left of the column mux or GND rail)
# goes from 0 down to the bottom of the address flops
for i in range(self.row_addr_size):
x_offset = self.start_of_left_central_bus + i * self.m2_pitch
x_offset = self.start_of_left_central_bus + i * self.m2_pitch
name = "A[{}]".format(i)
self.central_line_xoffset[name]=x_offset
# Add a label pin for LVS correspondence and visual help inspecting the rail.
@ -767,22 +776,37 @@ class bank(design.design):
""" Routing of sense amp output to tri_gate input """
for i in range(self.word_size):
# Connection of data_out of sense amp to data_ in of msf_data_out
tri_gate_in = self.tri_gate_array_inst.get_pin("in[{}]".format(i)).bc()
sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).rc() # rc to get enough overlap
sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).bc()
# if we need a bend or not
if tri_gate_in.x-sa_data_out.x>self.m2_pitch:
# We'll connect to the bottom of the SA pin
bendX = sa_data_out.x
else:
# We'll connect to the left of the SA pin
sa_data_out = self.sense_amp_array_inst.get_pin("data[{}]".format(i)).lc()
bendX = tri_gate_in.x - 3*self.m3_width
startY = self.tri_gate_array_inst.ll().y - 2*drc["minwidth_metal3"] + 0.5*self.m1_width
start = vector(tri_gate_in.x - 3 * drc["minwidth_metal3"], startY)
bendY = tri_gate_in.y - 2*self.m2_width
m3_min = vector([drc["minwidth_metal3"]] * 2)
mid1 = tri_gate_in.scale(1,0) + sa_data_out.scale(0,1) + m3_min.scale(-3, 1)
mid2 = sa_data_out + m3_min.scale(0.5, 1)
self.add_path("metal3", [start, mid1, mid2])
# Connection point of M2 and M3 paths, below the tri gate and
# to the left of the tri gate input
bend = vector(bendX, bendY)
mid3 = [tri_gate_in.x, startY]
self.add_path("metal2", [start, mid3, tri_gate_in])
# Connect an M2 path to the gate
mid3 = [tri_gate_in.x, bendY] # guarantee down then left
self.add_path("metal2", [bend, mid3, tri_gate_in])
offset = start - vector([0.5*drc["minwidth_metal3"]] * 2)
# connect up then right to sense amp
mid1 = vector(bendX,sa_data_out.y)
self.add_path("metal3", [bend, mid1, sa_data_out])
offset = bend - vector([0.5*drc["minwidth_metal3"]] * 2)
self.add_via(("metal2", "via2", "metal3"),offset)
def route_tri_gate_out(self):
@ -797,7 +821,8 @@ class bank(design.design):
height=tri_gate_out_position.y - self.min_point)
self.add_layout_pin(text="DATA[{}]".format(i),
layer="metal2",
offset=data_line_position)
offset=data_line_position,
height=2*self.m2_width)
def route_row_decoder(self):
""" Routes the row decoder inputs and supplies """
@ -830,16 +855,24 @@ class bank(design.design):
# Route the power and ground, but only BELOW the y=0 since the
# others are connected with the wordline driver.
# These must be on M3 to not interfere with column mux address pins.
for gnd_pin in self.row_decoder_inst.get_pins("gnd"):
if gnd_pin.uy()>0:
continue
driver_gnd_position = gnd_pin.rc()
gnd_rail_via = vector(self.gnd_x_offset, driver_gnd_position.y + 0.5*self.m1m2_via.width)
gnd_rail_position = vector(self.gnd_x_offset, driver_gnd_position.y)
self.add_path("metal1", [driver_gnd_position, gnd_rail_position])
self.add_via(layers=("metal1","via1","metal2"),
offset=gnd_rail_via,
rotate=270)
decoder_gnd_position = gnd_pin.rc()
via_position = decoder_gnd_position + vector(0.5*self.m1m2_via.height+drc["metal2_to_metal2"],0)
gnd_rail_position = vector(self.gnd_x_offset, decoder_gnd_position.y)
self.add_path("metal1", [decoder_gnd_position, via_position])
self.add_path("metal3", [via_position, gnd_rail_position])
self.add_center_via(layers=("metal1","via1","metal2"),
offset=via_position,
rotate=90)
self.add_center_via(layers=("metal2","via2","metal3"),
offset=via_position,
rotate=90)
self.add_center_via(layers=("metal2","via2","metal3"),
offset=gnd_rail_position,
rotate=270)
# route the vdd rails
for vdd_pin in self.row_decoder_inst.get_pins("vdd"):
@ -894,15 +927,15 @@ class bank(design.design):
return
# Connect the select lines to the column mux
# These must be in metal3 so that they don't overlap any gnd lines from decoders
for i in range(2**self.col_addr_size):
name = "sel[{}]".format(i)
col_addr_line_position = self.col_mux_array_inst.get_pin(name).lc()
wire_offset = vector(self.central_line_xoffset[name]+self.m2_width, col_addr_line_position.y)
self.add_path("metal1", [col_addr_line_position,wire_offset])
contact_offset = wire_offset - vector(0,0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=contact_offset,
rotate=90)
mux_addr_position = self.col_mux_array_inst.get_pin(name).lc()
wire_position = vector(self.central_line_xoffset[name]+0.5*self.m2_width, mux_addr_position.y)
self.add_path("metal1", [wire_position,mux_addr_position])
self.add_center_via(layers=("metal1", "via1", "metal2"),
offset=wire_position,
rotate=90)
# Take care of the column address decoder routing
# If there is a 2:4 decoder for column select lines
@ -950,13 +983,11 @@ class bank(design.design):
mid_position = vector(in_position.x,dout_position.y)
self.add_path("metal3",[dout_position, mid_position, in_position])
dout_via = self.msf_address_inst.get_pin("dout[{}]".format(ff_index)).lr()
in_via = self.col_decoder_inst.get_pin("in[{}]".format(i)).ul()
self.add_via(layers=("metal2", "via2", "metal3"),
offset=dout_via,
rotate=90)
self.add_via(layers=("metal2", "via2", "metal3"),
offset=in_via)
self.add_center_via(layers=("metal2", "via2", "metal3"),
offset=dout_position,
rotate=90)
self.add_center_via(layers=("metal2", "via2", "metal3"),
offset=in_position)
@ -1000,13 +1031,17 @@ class bank(design.design):
# Create the address input pins
for i in range(self.addr_size):
msf_din_position = self.msf_address_inst.get_pin("din[{}]".format(i)).ll()
msf_din_pins = self.msf_address_inst.get_pins("din[{}]".format(i))
for pin in msf_din_pins:
if pin.layer=="metal3":
msf_din_position=pin.ll()
break
address_position = vector(self.left_vdd_x_offset, msf_din_position.y)
self.add_layout_pin(text="ADDR[{}]".format(i),
layer="metal2",
layer="metal3",
offset=address_position,
width=msf_din_position.x - self.left_vdd_x_offset,
height=drc["minwidth_metal2"])
width=msf_din_position.x - self.left_vdd_x_offset)
for i in range(self.row_addr_size):
@ -1087,12 +1122,12 @@ class bank(design.design):
data_name = "data[{}]".format(i)
data_pin = self.sense_amp_array_inst.get_pin(data_name)
self.add_label(text="data_out[{}]".format(i),
layer="metal2",
layer="metal3",
offset=data_pin.ll())
def route_control_lines(self):
""" Rout the control lines of the entire bank """
""" Route the control lines of the entire bank """
# Make a list of tuples that we will connect.
# From control signal to the module pin
@ -1107,16 +1142,15 @@ class bank(design.design):
connection.append((self.prefix+"s_en", self.sense_amp_array_inst.get_pin("en").lc()))
for (control_signal, pin_position) in connection:
control_x_offset = self.central_line_xoffset[control_signal] + self.m2_width
control_position = vector(control_x_offset, pin_position.y)
control_position = vector(self.central_line_xoffset[control_signal] + 0.5*self.m2_width, pin_position.y)
self.add_path("metal1", [control_position, pin_position])
via_offset = vector(control_x_offset, pin_position.y - 0.5*drc["minwidth_metal2"])
self.add_via(layers=("metal1", "via1", "metal2"),
offset=via_offset,
rotate=90)
#via_offset = vector(control_x_offset, pin_position.y - 0.5*drc["minwidth_metal2"])
self.add_center_via(layers=("metal1", "via1", "metal2"),
offset=control_position,
rotate=90)
# clk to msf address
control_signal = self.prefix+"clk"
control_signal = self.prefix+"clk_buf"
pin_position = self.msf_address_inst.get_pin("clk").uc()
mid_position = pin_position + vector(0,self.m1_pitch)
control_x_offset = self.central_line_xoffset[control_signal]
@ -1127,7 +1161,7 @@ class bank(design.design):
offset=control_via_position)
# clk to wordline_driver
control_signal = self.prefix+"clk"
control_signal = self.prefix+"clk_buf"
pin_position = self.wordline_driver_inst.get_pin("en").uc()
mid_position = pin_position + vector(0,self.m1_pitch)
control_x_offset = self.central_line_xoffset[control_signal]

View File

@ -217,9 +217,12 @@ def run_lvs(name, gds_name, sp_name):
test = re.compile("WARNING:")
extwarnings = filter(test.search, results)
for e in extwarnings:
debug.error(e.strip("\n"))
debug.warning(e.strip("\n"))
ext_errors = len(exterrors) + len(extwarnings)
# MRG - 9/26/17 - Change this to exclude warnings because of
# multiple labels on different pins in column mux.
ext_errors = len(exterrors)
ext_warnings = len(extwarnings)
# also check the output file
f = open(outfile, "r")
@ -234,7 +237,8 @@ def run_lvs(name, gds_name, sp_name):
out_errors = len(stdouterrors)
return summary_errors + out_errors + ext_errors
total_errors = summary_errors + out_errors + ext_errors
return total_errors
def run_pex(name, gds_name, sp_name, output=None):

View File

@ -73,7 +73,7 @@ class delay():
self.sf.write("* SRAM output loads\n")
for i in range(self.word_size):
self.sf.write("CD{0} D[{0}] 0 {1}f\n".format(i,load))
self.sf.write("CD{0} d[{0}] 0 {1}f\n".format(i,load))
# add access transistors for data-bus
self.sf.write("* Transmission Gates for data-bus and control signals\n")
@ -85,12 +85,12 @@ class delay():
if i == self.probe_data:
stimuli.gen_data(stim_file=self.sf,
clk_times=self.cycle_times,
sig_name="DATA[{0}]".format(i),
sig_name="data[{0}]".format(i),
period=period,
slew=slew)
else:
stimuli.gen_constant(stim_file=self.sf,
sig_name="D[{0}]".format(i),
sig_name="d[{0}]".format(i),
v_val=self.gnd)
stimuli.gen_addr(self.sf,
@ -127,7 +127,7 @@ class delay():
self.sf.write("* Measure statements for delay and power\n")
trig_name = "clk"
targ_name = "{0}".format("D[{0}]".format(self.probe_data))
targ_name = "{0}".format("d[{0}]".format(self.probe_data))
trig_val = targ_val = 0.5 * self.vdd
# add measure statments for delay0
# delay the target to measure after the negetive edge

View File

@ -181,13 +181,13 @@ def gen_csb(stim_file, clk_times, period, slew):
""" Generates the PWL CSb signal"""
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
values = [1, 0, 0, 0, 0, 0, 0, 0, 1]
gen_pwl(stim_file, "CSb", clk_times, values, period, slew, 0.05)
gen_pwl(stim_file, "csb", clk_times, values, period, slew, 0.05)
def gen_web(stim_file, clk_times, period, slew):
""" Generates the PWL WEb signal"""
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
values = [1, 0, 0, 0, 1, 0, 0, 1, 1]
gen_pwl(stim_file, "WEb", clk_times, values, period, slew, 0.05)
gen_pwl(stim_file, "web", clk_times, values, period, slew, 0.05)
values = [1, 0, 0, 0, 1, 0, 0, 1, 1]
gen_pwl(stim_file, "acc_en", clk_times, values, period, slew, 0)
@ -198,7 +198,7 @@ def gen_oeb(stim_file, clk_times, period, slew):
""" Generates the PWL WEb signal"""
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
values = [1, 1, 1, 1, 0, 1, 1, 0, 1]
gen_pwl(stim_file, "OEb", clk_times, values, period, slew, 0.05)
gen_pwl(stim_file, "oeb", clk_times, values, period, slew, 0.05)

View File

@ -17,7 +17,7 @@ class contact(design.design):
dimensions[0],
dimensions[1])
design.design.__init__(self, name)
debug.info(3, "create contact object {0}".format(name))
debug.info(4, "create contact object {0}".format(name))
self.layer_stack = layer_stack
self.dimensions = dimensions

View File

@ -47,7 +47,6 @@ class control_logic(design.design):
self.nor2 = nor_2()
self.add_mod(self.nor2)
# Special gates: inverters for buffering
self.inv = self.inv1 = pinv()
self.add_mod(self.inv1)
@ -81,20 +80,20 @@ class control_logic(design.design):
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
# For different layer width vias
self.m1m2_offset_fix = vector(0,0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]))
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
# Have the cell gap leave enough room to route an M1 wire.
# Have the cell gap leave enough room to route an M2 wire.
# Some cells may have pwell/nwell spacing problems too when the wells are different heights.
self.cell_gap = max(self.m1_pitch,drc["pwell_to_nwell"])
# This corrects the offset pitch difference between M2 and M1
self.offset_fix = vector(0.5*(drc["minwidth_metal2"]-drc["minwidth_metal1"]),0)
self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])
# Amount to shift a via from center-line path routing to it's offset
self.m1m2_via_offset = vector(self.m1m2_via.height,-0.5*drc["minwidth_metal2"])
self.m2m3_via_offset = vector(self.m2m3_via.height,-0.5*drc["minwidth_metal3"])
# Amount to shift a 90 degree rotated via from center-line path routing to it's offset
self.m1m2_via_offset = vector(self.m1m2_via.first_layer_height,-0.5*drc["minwidth_metal2"])
self.m2m3_via_offset = vector(self.m2m3_via.first_layer_height,-0.5*drc["minwidth_metal3"])
# First RAIL Parameters: gnd, oe, oebar, cs, we, clk_buf, clk_bar
self.rail_1_start_x = 0
@ -127,6 +126,8 @@ class control_logic(design.design):
self.add_rbl(0)
self.add_layout_pins()
self.add_lvs_correspondence_points()
self.height = max(self.replica_bitline.width, 3 * self.inv1.height, self.msf_offset.y)
self.width = self.replica_bitline_offset.x + self.replica_bitline.height
@ -146,10 +147,10 @@ class control_logic(design.design):
def add_control_flops(self):
""" Add the control signal flops for OEb, WEb, CSb. """
self.msf_offset = vector(0, self.inv.height+self.msf_control.width+2*self.m2_pitch)
self.msf=self.add_inst(name="msf_control",
mod=self.msf_control,
offset=self.msf_offset,
rotate=270)
self.msf_inst=self.add_inst(name="msf_control",
mod=self.msf_control,
offset=self.msf_offset,
rotate=270)
# don't change this order. This pins are meant for internal connection of msf array inside the control logic.
# These pins are connecting the msf_array inside of control_logic.
temp = ["oeb", "csb", "web",
@ -177,20 +178,19 @@ class control_logic(design.design):
pin_set = ["oeb","csb","web"]
for (i,pin_name) in zip(range(3),pin_set):
subpin_name="din[{}]".format(i)
pin=self.msf.get_pin(subpin_name)
#pin=self.msf_control.get_pin("din[{}]".format(i))
self.add_layout_pin(text=pin_name,
layer="metal2",
offset=pin.ll(),
width=pin.width(),
height=pin.height())
pins=self.msf_inst.get_pins(subpin_name)
for pin in pins:
if pin.layer=="metal3":
self.add_layout_pin(text=pin_name,
layer="metal3",
offset=pin.ll(),
width=pin.width(),
height=pin.height())
pin=self.clk_inv1.get_pin("A")
self.add_layout_pin(text="clk",
layer="metal1",
offset=pin.ll(),
width=pin.width(),
height=pin.height())
offset=pin.ll())
pin=self.clk_inv1.get_pin("gnd")
self.add_layout_pin(text="gnd",
@ -354,31 +354,33 @@ class control_logic(design.design):
width=drc["minwidth_metal2"],
height=control_rail_height)
else:
self.add_rect(layer="metal2",
offset=offset,
width=drc["minwidth_metal2"],
height=control_rail_height)
# just for LVS correspondence...
self.add_label_pin(text=self.rail_1_names[i],
layer="metal2",
offset=offset,
width=drc["minwidth_metal2"],
height=control_rail_height)
self.rail_1_x_offsets[self.rail_1_names[i]]=offset.x + 0.5*drc["minwidth_metal2"] # center offset
# pins are in order ["oeb","csb","web"] # 0 1 2
self.connect_rail_from_left_m2m3(self.msf,"dout_bar[0]","oe")
self.connect_rail_from_left_m2m3(self.msf,"dout[0]","oe_bar")
self.connect_rail_from_left_m2m3(self.msf,"dout_bar[1]","cs")
self.connect_rail_from_left_m2m3(self.msf,"dout_bar[2]","we")
self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[0]","oe")
self.connect_rail_from_left_m2m3(self.msf_inst,"dout[0]","oe_bar")
self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[1]","cs")
self.connect_rail_from_left_m2m3(self.msf_inst,"dout_bar[2]","we")
# Connect the gnd and vdd of the control
gnd_pins = self.msf.get_pins("gnd")
gnd_pins = self.msf_inst.get_pins("gnd")
for p in gnd_pins:
if p.layer != "metal2":
continue
gnd_pin = p.rc()
gnd_rail_position = vector(self.rail_1_x_offsets["gnd"], gnd_pin.y)
self.add_wire(("metal1","via1","metal2"),[gnd_pin, gnd_rail_position, gnd_rail_position - vector(0,self.m2_pitch)])
self.add_via(layers=("metal1","via1","metal2"),
offset=gnd_pin + self.m1m2_via_offset,
self.add_wire(("metal3","via2","metal2"),[gnd_pin, gnd_rail_position, gnd_rail_position - vector(0,self.m2_pitch)])
self.add_via(layers=("metal2","via2","metal3"),
offset=gnd_pin + self.m2m3_via_offset,
rotate=90)
vdd_pins = self.msf.get_pins("vdd")
vdd_pins = self.msf_inst.get_pins("vdd")
for p in vdd_pins:
if p.layer != "metal1":
continue
@ -449,6 +451,7 @@ class control_logic(design.design):
in_pin = inst.get_pin(pin).rc()
rail_position = vector(self.rail_1_x_offsets[rail], in_pin.y)
self.add_wire(("metal3","via2","metal2"),[in_pin, rail_position, rail_position - vector(0,self.m2_pitch)])
# This via is needed for clk_bar, but is extraneous for the output signals
self.add_via(layers=("metal1","via1","metal2"),
offset=in_pin + self.m1m2_via_offset,
rotate=90)
@ -501,29 +504,40 @@ class control_logic(design.design):
self.connect_rail_from_left_m2m3(self.clk_bar,"Z","clk_bar")
# clk_buf to msf control flops
msf_clk_pin = self.msf.get_pin("clk").bc()
msf_clk_pin = self.msf_inst.get_pin("clk").bc()
mid1 = msf_clk_pin - vector(0,self.m2_pitch)
clk_buf_rail_position = vector(self.rail_1_x_offsets["clk_buf"], mid1.y)
# route on M2 to allow vdd connection
self.add_wire(("metal2","via1","metal1"),[msf_clk_pin, mid1, clk_buf_rail_position])
def connect_pin_to_output_pin(self, inst, pin_name, out_name):
def connect_right_pin_to_output_pin(self, inst, pin_name, out_name):
""" Create an output pin on the bottom side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name).center()
out_pin = inst.get_pin(pin_name).ur()
self.add_via(layers=("metal1","via1","metal2"),
offset=out_pin + self.m1m2_via_offset,
offset=out_pin + vector(self.m1m2_via.height,-self.m1m2_via.first_layer_width) - self.m1m2_offset_fix,
rotate=90)
self.add_layout_pin(text=out_name,
layer="metal2",
offset=out_pin.scale(1,0),
height=out_pin.y)
def connect_left_pin_to_output_pin(self, inst, pin_name, out_name):
""" Create an output pin on the bottom side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name).ul()
self.add_via(layers=("metal1","via1","metal2"),
offset=out_pin + vector(self.m1m2_via.height,-self.m1m2_via.first_layer_width) - self.m1m2_offset_fix,
rotate=90)
self.add_layout_pin(text=out_name,
layer="metal2",
offset=out_pin.scale(1,0),
height=out_pin.y)
def add_output_routing(self):
""" Output pin routing """
self.connect_pin_to_output_pin(self.tri_en, "Z", "tri_en")
self.connect_pin_to_output_pin(self.tri_en_bar, "Z", "tri_en_bar")
self.connect_pin_to_output_pin(self.w_en, "Z", "w_en")
self.connect_pin_to_output_pin(self.s_en, "Z", "s_en")
self.connect_right_pin_to_output_pin(self.tri_en, "Z", "tri_en")
self.connect_right_pin_to_output_pin(self.tri_en_bar, "Z", "tri_en_bar")
self.connect_right_pin_to_output_pin(self.w_en, "Z", "w_en")
self.connect_left_pin_to_output_pin(self.s_en, "Z", "s_en")
def add_supply_routing(self):
@ -614,3 +628,24 @@ class control_logic(design.design):
width=rows_end-rows_start,
height=well_width)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist.
"""
pin=self.clk_inv1.get_pin("Z")
self.add_label_pin(text="clk1_bar",
layer="metal1",
offset=pin.ll(),
height=pin.height(),
width=pin.width())
pin=self.clk_inv2.get_pin("Z")
self.add_label_pin(text="clk2",
layer="metal1",
offset=pin.ll(),
height=pin.height(),
width=pin.width())

View File

@ -43,11 +43,11 @@ class instance(geometry):
self.compute_boundary(offset,mirror,rotate)
debug.info(3, "creating instance: " + self.name)
debug.info(4, "creating instance: " + self.name)
def gds_write_file(self, newLayout):
"""Recursively writes all the sub-modules in this instance"""
debug.info(3, "writing instance: " + self.name)
debug.info(4, "writing instance: " + self.name)
# make sure to write out my module/structure
# (it will only be written the first time though)
self.mod.gds_write_file(self.gds)
@ -172,7 +172,7 @@ class path(geometry):
def gds_write_file(self, newLayout):
"""Writes the path to GDS"""
debug.info(3, "writing path (" + str(self.layerNumber) + "): " + self.coordinates)
debug.info(4, "writing path (" + str(self.layerNumber) + "): " + self.coordinates)
newLayout.addPath(layerNumber=self.layerNumber,
purposeNumber=0,
coordinates=self.coordinates,
@ -205,11 +205,11 @@ class label(geometry):
self.size = 0
debug.info(3,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset))
debug.info(4,"creating label " + self.text + " " + str(self.layerNumber) + " " + str(self.offset))
def gds_write_file(self, newLayout):
"""Writes the text label to GDS"""
debug.info(3, "writing label (" + str(self.layerNumber) + "): " + self.text)
debug.info(4, "writing label (" + str(self.layerNumber) + "): " + self.text)
newLayout.addText(text=self.text,
layerNumber=self.layerNumber,
purposeNumber=0,
@ -238,13 +238,13 @@ class rectangle(geometry):
self.width = self.size.x
self.height = self.size.y
debug.info(3, "creating rectangle (" + str(self.layerNumber) + "): "
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
def gds_write_file(self, newLayout):
"""Writes the rectangular shape to GDS"""
debug.info(3, "writing rectangle (" + str(self.layerNumber) + "):"
debug.info(4, "writing rectangle (" + str(self.layerNumber) + "):"
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
newLayout.addBox(layerNumber=self.layerNumber,
purposeNumber=0,

View File

@ -91,9 +91,6 @@ class hierarchical_decoder(design.design):
self.metal2_spacing = self.metal2_extend_contact + drc["metal2_to_metal2"]
self.metal2_pitch = self.metal2_spacing + drc["minwidth_metal2"]
self.via_shift = (self.m1m2_via.second_layer_width - self.m1m2_via.first_layer_width) / 2
# used to shift contact when connecting to NAND3 C pin down
self.contact_shift = (self.m1m2_via.first_layer_width - self.m1m2_via.contact_width) / 2
self.predec_groups = [] # This array is a 2D array.
@ -483,19 +480,17 @@ class hierarchical_decoder(design.design):
yoffset_A = current_inv_height + a_pin.by()
yoffset_B = current_inv_height + b_pin.by()
yoffset_C = current_inv_height + c_pin.by()
contact_C_yoffset = yoffset_C - self.contact_shift
else:
base = current_inv_height + self.inv.height - drc["minwidth_metal1"]
yoffset_A = base - a_pin.by()
yoffset_B = base - b_pin.by()
yoffset_C = base - c_pin.by()
contact_C_yoffset = yoffset_C
row_index = row_index + 1
self.connect_rail(vector(self.rail_x_offsets[index_A], yoffset_A))
self.connect_rail(vector(self.rail_x_offsets[index_B], yoffset_B))
self.connect_rail(vector(self.rail_x_offsets[index_C], yoffset_C)) # contact_C_y_offset
self.connect_rail(vector(self.rail_x_offsets[index_C], yoffset_C))
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """

View File

@ -116,12 +116,11 @@ class hierarchical_predecode(design.design):
name = "Xpre_inv[{0}]".format(inv_num)
if (inv_num % 2 == 0):
y_off = inv_num * (self.inv.height)
offset = vector(self.x_off_inv_1, y_off)
mirror = "R0"
else:
y_off = (inv_num + 1) * (self.inv.height)
offset = vector(self.x_off_inv_1, y_off)
mirror="MX"
offset = vector(self.x_off_inv_1, y_off)
self.add_inst(name=name,
mod=self.inv,
offset=offset,
@ -133,62 +132,62 @@ class hierarchical_predecode(design.design):
def add_output_inverters(self):
""" Create inverters for the inverted output decode signals. """
self.decode_out_positions = []
self.inv_inst = []
z_pin = self.inv.get_pin("Z")
for inv_num in range(self.number_of_outputs):
name = "Xpre2x4_nand_inv[{}]".format(inv_num)
name = "Xpre_nand_inv[{}]".format(inv_num)
if (inv_num % 2 == 0):
y_factor = inv_num
y_off = inv_num * self.inv.height
mirror = "R0"
y_dir = 1
else:
y_factor =inv_num + 1
y_off =(inv_num + 1)*self.inv.height
mirror = "MX"
y_dir = -1
base = vector(self.x_off_inv_2, self.inv.height * y_factor)
self.add_inst(name=name,
mod=self.inv,
offset=base,
mirror=mirror)
offset = vector(self.x_off_inv_2, y_off)
self.inv_inst.append(self.add_inst(name=name,
mod=self.inv,
offset=offset,
mirror=mirror))
self.connect_inst(["Z[{}]".format(inv_num),
"out[{}]".format(inv_num),
"vdd", "gnd"])
z_pin = self.inv.get_pin("Z")
z_pin = self.inv_inst[-1].get_pin("Z")
self.add_layout_pin(text="out[{}]".format(inv_num),
layer="metal1",
offset=base+z_pin.ll().scale(1,y_dir),
offset=z_pin.ll(),
width=z_pin.width(),
height=z_pin.height()*y_dir)
height=z_pin.height())
def add_nand(self,connections):
""" Create the NAND stage for the decodes """
z_pin = self.nand.get_pin("Z")
a_pin = self.inv.get_pin("A")
self.nand_inst = []
for nand_input in range(self.number_of_outputs):
inout = str(self.number_of_inputs)+"x"+str(self.number_of_outputs)
name = "Xpre{0}_nand[{1}]".format(inout,nand_input)
rect_height = z_pin.uy()-a_pin.by()
if (nand_input % 2 == 0):
y_off = nand_input * (self.nand.height)
y_off = nand_input * self.inv.height
mirror = "R0"
rect_offset = vector(self.x_off_nand + self.nand.width,
y_off + z_pin.uy() - rect_height)
else:
y_off = (nand_input + 1) * (self.nand.height)
y_off = (nand_input + 1) * self.inv.height
mirror = "MX"
rect_offset =vector(self.x_off_nand + self.nand.width,
y_off - z_pin.uy())
self.add_inst(name=name,
mod=self.nand,
offset=[self.x_off_nand, y_off],
mirror=mirror)
self.add_rect(layer="metal1",
offset=rect_offset,
width=self.metal1_width,
height=rect_height)
offset = vector(self.x_off_nand, y_off)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand,
offset=offset,
mirror=mirror))
self.connect_inst(connections[nand_input])
z_pin = self.nand_inst[nand_input].get_pin("Z")
a_pin = self.inv_inst[nand_input].get_pin("A")
y_min = min(z_pin.by(),a_pin.by())
y_max = max(z_pin.uy(),a_pin.uy())
offset = vector(z_pin.rx(),y_min)
self.add_rect(layer="metal1",
offset=offset,
width=self.metal1_width,
height=y_max-y_min)
def route(self):
self.route_input_inverters()

View File

@ -104,8 +104,12 @@ class layout:
return inst
return None
def add_rect(self, layer, offset, width, height):
def add_rect(self, layer, offset, width=0, height=0):
"""Adds a rectangle on a given layer,offset with width and height"""
if width==0:
width=drc["minwidth_{}".format(layer)]
if height==0:
height=drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
layerNumber = techlayer[layer]
if layerNumber >= 0:
@ -116,10 +120,18 @@ class layout:
def get_pin(self, text):
""" Return the pin or list of pins """
debug.check(len(self.pin_map[text])==1,"Should use a pin iterator since more than one pin.")
# If we have one pin, return it and not the list.
# Otherwise, should use get_pins()
return self.pin_map[text][0]
try:
if len(self.pin_map[text])>1:
debug.warning("Should use a pin iterator since more than one pin {}".format(text))
# If we have one pin, return it and not the list.
# Otherwise, should use get_pins()
return self.pin_map[text][0]
except Exception as e:
#print e
self.gds_write("missing_pin.gds")
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1)
def get_pins(self, text):
""" Return a pin list (instead of a single pin) """
@ -136,6 +148,30 @@ class layout:
new_name = pin.name
self.add_layout_pin(new_name, pin.layer, pin.ll(), pin.width(), pin.height())
def add_center_layout_pin(self, text, layer, start, end):
""" Creates a path like pin with center-line convention """
debug.check(start.x==end.x or start.y==end.y,"Cannot have a non-manhatten layout pin.")
minwidth_layer = drc["minwidth_{}".format(layer)]
# one of these will be zero
width = max(start.x,end.x) - min(start.x,end.x)
height = max(start.y,end.y) - min(start.y,end.y)
ll_offset = vector(min(start.x,end.x),min(start.y,end.y))
# Shift it down 1/2 a width in the 0 dimension
if height==0:
ll_offset -= vector(0,0.5*minwidth_layer)
if width==0:
ll_offset -= vector(0.5*minwidth_layer,0)
# This makes sure it is long enough, but also it is not 0 width!
height = max(minwidth_layer,height)
width = max(minwidth_layer,width)
self.add_layout_pin(text, layer, ll_offset, width, height)
def add_layout_pin(self, text, layer, offset, width=None, height=None):
"""Create a labeled pin """
@ -186,6 +222,7 @@ class layout:
def add_label(self, text, layer, offset=[0,0],zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level"""
# negative layers indicate "unused" layers in a given technology
debug.info(5,"add label " + str(text) + " " + layer + " " + str(offset))
layerNumber = techlayer[layer]
if layerNumber >= 0:
self.objs.append(geometry.label(text, layerNumber, offset, zoom))
@ -195,7 +232,7 @@ class layout:
def add_path(self, layer, coordinates, width=None):
"""Connects a routing path on given layer,coordinates,width."""
debug.info(3,"add path " + str(layer) + " " + str(coordinates))
debug.info(4,"add path " + str(layer) + " " + str(coordinates))
import path
# NOTE: (UNTESTED) add_path(...) is currently not used
# negative layers indicate "unused" layers in a given technology
@ -215,7 +252,7 @@ class layout:
the coordinates.
"""
import route
debug.info(3,"add route " + str(layers) + " " + str(coordinates))
debug.info(4,"add route " + str(layers) + " " + str(coordinates))
# add an instance of our path that breaks down into rectangles and contacts
route.route(obj=self,
layer_stack=layers,
@ -236,8 +273,17 @@ class layout:
return self.add_via(layers=layers,
offset=offset,
size=size,
mirror=mirror,rotate=rotate)
mirror=mirror,
rotate=rotate)
def add_center_contact(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
""" This is just an alias for a via."""
return self.add_centered_via(layers=layers,
offset=offset,
size=size,
mirror=mirror,
rotate=rotate)
def add_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
""" Add a three layer via structure. """
import contact
@ -253,6 +299,39 @@ class layout:
self.connect_inst([])
return via
def add_center_via(self, layers, offset, size=[1,1], mirror="R0", rotate=0):
""" Add a three layer via structure by the center coordinate accounting for mirroring and rotation. """
import contact
via = contact.contact(layer_stack=layers,
dimensions=size)
debug.check(mirror=="R0","Use rotate to rotate vias instead of mirror.")
height = via.height
width = via.width
if rotate==0:
corrected_offset = offset + vector(-0.5*width,-0.5*height)
elif rotate==90:
corrected_offset = offset + vector(0.5*height,-0.5*width)
elif rotate==180:
corrected_offset = offset + vector(-0.5*width,0.5*height)
elif rotate==270:
corrected_offset = offset + vector(-0.5*height,0.5*width)
else:
debug.error("Invalid rotation argument.",-1)
self.add_mod(via)
self.add_inst(name=via.name,
mod=via,
offset=corrected_offset,
mirror=mirror,
rotate=rotate)
# We don't model the logical connectivity of wires/paths
self.connect_inst([])
return via
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
"""Adds a ptx module to the design."""
import ptx
@ -278,14 +357,14 @@ class layout:
reader = gdsMill.Gds2reader(self.gds)
reader.loadFromFile(self.gds_file)
else:
debug.info(3, "creating structure %s" % self.name)
debug.info(4, "creating structure %s" % self.name)
self.gds = gdsMill.VlsiLayout(name=self.name, units=GDS["unit"])
def print_gds(self, gds_file=None):
"""Print the gds file (not the vlsi class) to the terminal """
if gds_file == None:
gds_file = self.gds_file
debug.info(3, "Printing %s" % gds_file)
debug.info(4, "Printing %s" % gds_file)
arrayCellLayout = gdsMill.VlsiLayout(units=GDS["unit"])
reader = gdsMill.Gds2reader(arrayCellLayout, debugToTerminal=1)
reader.loadFromFile(gds_file)

View File

@ -80,12 +80,13 @@ class ms_flop_array(design.design):
width=gnd_pin.width(),
height=gnd_pin.height())
din_pin = self.ms_inst[i].get_pin("din")
self.add_layout_pin(text="din[{}]".format(i),
layer="metal2",
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
din_pins = self.ms_inst[i].get_pins("din")
for din_pin in din_pins:
self.add_layout_pin(text="din[{}]".format(i),
layer=din_pin.layer,
offset=din_pin.ll(),
width=din_pin.width(),
height=din_pin.height())
dout_pin = self.ms_inst[i].get_pin("dout")
self.add_layout_pin(text="dout[{}]".format(i),

View File

@ -63,28 +63,20 @@ class nand_2(design.design):
# transistors are created here but not yet placed or added as a module
def create_ptx(self):
""" Add required modules """
self.nmos1 = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="nmos")
self.add_mod(self.nmos1)
self.nmos2 = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="nmos")
self.add_mod(self.nmos2)
self.nmos = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="nmos")
self.add_mod(self.nmos)
self.pmos1 = ptx(width=self.pmos_size,
mults=self.tx_mults,
tx_type="pmos")
self.add_mod(self.pmos1)
self.pmos2 = ptx(width=self.pmos_size,
mults=self.tx_mults,
tx_type="pmos")
self.add_mod(self.pmos2)
self.pmos = ptx(width=self.pmos_size,
mults=self.tx_mults,
tx_type="pmos")
self.add_mod(self.pmos)
def setup_layout_constants(self):
""" Calculate the layout constraints """
self.well_width = self.pmos1.active_position.x \
+ 2 * self.pmos1.active_width \
self.well_width = self.pmos.active_position.x \
+ 2 * self.pmos.active_width \
+ drc["active_to_body_active"] + \
drc["well_enclosure_active"]
@ -116,44 +108,44 @@ class nand_2(design.design):
# determines the spacing between the edge and nmos (rail to active
# metal or poly_to_poly spacing)
edge_to_nmos = max(drc["metal1_to_metal1"]
- self.nmos1.active_contact_positions[0].y,
- self.nmos.active_contact_positions[0].y,
0.5 * (drc["poly_to_poly"] - drc["minwidth_metal1"])
- self.nmos1.poly_positions[0].y)
- self.nmos.poly_positions[0].y)
# determine the position of the first transistor from the left
self.nmos_position1 = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
offset = self.nmos_position1+ vector(0,self.nmos1.height)
offset = self.nmos_position1+ vector(0,self.nmos.height)
self.add_inst(name="nmos1",
mod=self.nmos1,
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["Z", "A", "net1", "gnd"])
self.nmos_position2 = vector(self.nmos2.active_width - self.nmos2.active_contact.width,
self.nmos_position2 = vector(self.nmos.active_width - self.nmos.active_contact.width,
self.nmos_position1.y)
offset = self.nmos_position2 + vector(0,self.nmos2.height)
offset = self.nmos_position2 + vector(0,self.nmos.height)
self.add_inst(name="nmos2",
mod=self.nmos2,
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["net1", "B", "gnd", "gnd"])
# determines the spacing between the edge and pmos
edge_to_pmos = max(drc["metal1_to_metal1"] \
- self.pmos1.active_contact_positions[0].y,
- self.pmos.active_contact_positions[0].y,
0.5 * drc["poly_to_poly"] - 0.5 * drc["minwidth_metal1"] \
- self.pmos1.poly_positions[0].y)
- self.pmos.poly_positions[0].y)
self.pmos_position1 = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
- edge_to_pmos - self.pmos1.height)
- edge_to_pmos - self.pmos.height)
self.add_inst(name="pmos1",
mod=self.pmos1,
mod=self.pmos,
offset=self.pmos_position1)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos_position2 = vector(self.nmos_position2.x, self.pmos_position1.y)
self.add_inst(name="pmos2",
mod=self.pmos2,
mod=self.pmos,
offset=self.pmos_position2)
self.connect_inst(["Z", "B", "vdd", "vdd"])
@ -162,20 +154,20 @@ class nand_2(design.design):
# create well contacts
layer_stack = ("active", "contact", "metal1")
xoffset = (self.nmos_position2.x + self.pmos1.active_position.x
+ self.pmos1.active_width + drc["active_to_body_active"])
xoffset = (self.nmos_position2.x + self.pmos.active_position.x
+ self.pmos.active_width + drc["active_to_body_active"])
yoffset = (self.pmos_position1.y +
self.pmos1.active_contact_positions[0].y)
self.pmos.active_contact_positions[0].y)
offset = self.nwell_contact_position = vector(xoffset, yoffset)
self.pwell_contact=self.add_contact(layer_stack,offset,(1,self.nmos1.num_of_tacts))
self.pwell_contact=self.add_contact(layer_stack,offset,(1,self.nmos.num_contacts))
xoffset = (self.nmos_position2.x + self.nmos1.active_position.x
+ self.nmos1.active_width + drc["active_to_body_active"])
yoffset = (self.nmos_position1.y + self.nmos1.height
- self.nmos1.active_contact_positions[0].y
- self.nmos1.active_contact.height)
xoffset = (self.nmos_position2.x + self.nmos.active_position.x
+ self.nmos.active_width + drc["active_to_body_active"])
yoffset = (self.nmos_position1.y + self.nmos.height
- self.nmos.active_contact_positions[0].y
- self.nmos.active_contact.height)
offset = self.pwell_contact_position = vector(xoffset, yoffset)
self.nwell_contact=self.add_contact(layer_stack,offset,(1,self.pmos1.num_of_tacts))
self.nwell_contact=self.add_contact(layer_stack,offset,(1,self.pmos.num_contacts))
def connect_well_contacts(self):
""" Connect well contacts to vdd and gnd rail """
@ -189,7 +181,7 @@ class nand_2(design.design):
width=drc["minwidth_metal1"],
height=well_tap_length)
well_tap_length = self.nmos1.active_height
well_tap_length = self.nmos.active_height
offset = (self.pwell_contact_position.scale(1,0)
+ self.pwell_contact.second_layer_position.scale(1,0)
- self.pwell_contact.first_layer_position.scale(1,0))
@ -199,22 +191,22 @@ class nand_2(design.design):
def connect_rails(self):
""" Connect transistor pmos drains to vdd and nmos drains to gnd rail """
correct = vector(self.pmos1.active_contact.width - drc["minwidth_metal1"],
correct = vector(self.pmos.active_contact.width - drc["minwidth_metal1"],
0).scale(.5,0)
poffset = self.pmos_position1 + self.pmos1.active_contact_positions[0] + correct
poffset = self.pmos_position1 + self.pmos.active_contact_positions[0] + correct
temp_height = self.height - poffset.y
self.add_rect(layer="metal1",
offset=poffset, width=drc["minwidth_metal1"],
height=temp_height)
poffset = vector(2 * self.pmos_position2.x + correct.x
+ self.pmos2.active_contact_positions[0].x , poffset.y)
+ self.pmos.active_contact_positions[0].x , poffset.y)
self.add_rect(layer="metal1",
offset=poffset,
width=drc["minwidth_metal1"],
height=temp_height)
poffset = self.nmos_position1 + self.nmos1.active_contact_positions[0] + correct
poffset = self.nmos_position1 + self.nmos.active_contact_positions[0] + correct
self.add_rect(layer="metal1",
offset=poffset.scale(1,0),
width=drc["minwidth_metal1"],
@ -228,40 +220,40 @@ class nand_2(design.design):
def connect_poly(self):
""" poly connection """
yoffset_nmos1 = (self.nmos_position1.y
+ self.nmos1.poly_positions[0].y
+ self.nmos1.poly_height)
poly_length = (self.pmos_position1.y + self.pmos1.poly_positions[0].y
+ self.nmos.poly_positions[0].y
+ self.nmos.poly_height)
poly_length = (self.pmos_position1.y + self.pmos.poly_positions[0].y
- yoffset_nmos1 + drc["minwidth_poly"])
for position in self.pmos1.poly_positions:
for position in self.pmos.poly_positions:
offset = vector(position.x,
yoffset_nmos1 - 0.5 * drc["minwidth_poly"])
self.add_rect(layer="poly",
offset=offset, width=drc["minwidth_poly"],
height=poly_length)
self.add_rect(layer="poly",
offset=[offset.x + self.pmos1.active_contact.width + 2 * drc["minwidth_poly"],
offset=[offset.x + self.pmos.active_contact.width + 2 * drc["minwidth_poly"],
offset.y],
width=drc["minwidth_poly"],
height=poly_length)
def connect_drains(self):
""" Connect pmos and nmos drains. The output will be routed to this connection point. """
yoffset = self.nmos_position1.y + self.nmos1.active_contact_positions[0].y
drain_length = (self.height + self.pmos1.active_contact_positions[0].y
- yoffset - self.pmos1.height + 0.5 * drc["minwidth_metal2"])
yoffset = self.nmos_position1.y + self.nmos.active_contact_positions[0].y
drain_length = (self.height + self.pmos.active_contact_positions[0].y
- yoffset - self.pmos.height + 0.5 * drc["minwidth_metal2"])
for position in self.pmos1.active_contact_positions[1:][::2]:
for position in self.pmos.active_contact_positions[1:][::2]:
start = self.drain_position = vector(position.x + 0.5 * drc["minwidth_metal1"]
+ self.pmos_position2.x
+ self.pmos2.active_contact.first_layer_position.x
+ self.pmos2.active_contact.width / 2,
+ self.pmos.active_contact.first_layer_position.x
+ self.pmos.active_contact.width / 2,
yoffset)
mid1 = vector(start.x,
self.height - drc["minwidth_metal2"] - drc["metal2_to_metal2"] -
self.pmos_size - drc["metal1_to_metal1"] - 0.5 * drc["minwidth_metal1"])
end = vector(position.x + 0.5 * drc["minwidth_metal1"]
+ self.pmos2.active_contact.second_layer_position.x,
self.pmos_position1.y + self.pmos1.active_contact_positions[0].y)
+ self.pmos.active_contact.second_layer_position.x,
self.pmos_position1.y + self.pmos.active_contact_positions[0].y)
mid2 = vector(end.x, mid1.y)
self.add_path("metal1",[start, mid1, mid2, end])
@ -279,19 +271,19 @@ class nand_2(design.design):
def route_input_gate_A(self):
""" routing for input A """
xoffset = self.pmos1.poly_positions[0].x
xoffset = self.pmos.poly_positions[0].x
yoffset = (self.height
- (drc["minwidth_metal1"]
+ drc["metal1_to_metal1"]
+ self.pmos2.active_height
+ self.pmos.active_height
+ drc["metal1_to_metal1"]
+ self.pmos2.active_contact.second_layer_width))
+ self.pmos.active_contact.second_layer_width))
if (self.nmos_size == drc["minwidth_tx"]):
yoffset = (self.pmos_position1.y
+ self.pmos1.poly_positions[0].y
+ self.pmos.poly_positions[0].y
+ drc["poly_extend_active"]
- (self.pmos1.active_contact.height
- self.pmos1.active_height) / 2
- (self.pmos.active_contact.height
- self.pmos.active_height) / 2
- drc["metal1_to_metal1"]
- self.poly_contact.width)
@ -307,7 +299,7 @@ class nand_2(design.design):
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
height=self.poly_contact.first_layer_width)
input_length = (self.pmos1.poly_positions[0].x
input_length = (self.pmos.poly_positions[0].x
- self.poly_contact.height)
yoffset += self.poly_contact.via_layer_position.x
offset = self.input_position1 = vector(0, yoffset)
@ -319,16 +311,16 @@ class nand_2(design.design):
def route_input_gate_B(self):
""" routing for input B """
xoffset = (self.pmos2.poly_positions[0].x
xoffset = (self.pmos.poly_positions[0].x
+ self.pmos_position2.x + drc["minwidth_poly"])
yoffset = (drc["minwidth_metal1"]
+ drc["metal1_to_metal1"]
+ self.nmos2.active_height
+ self.nmos.active_height
+ drc["minwidth_metal1"])
if (self.nmos_size == drc["minwidth_tx"]):
yoffset = (self.nmos_position1.y
+ self.nmos1.poly_positions[0].y
+ self.nmos1.poly_height
+ self.nmos.poly_positions[0].y
+ self.nmos.poly_height
+ drc["metal1_to_metal1"])
offset = [xoffset, yoffset]
self.add_contact(layers=("poly", "contact", "metal1"),
@ -336,7 +328,7 @@ class nand_2(design.design):
size=(1,1),
rotate=90)
input_length = self.pmos2.poly_positions[0].x - self.poly_contact.height
input_length = self.pmos.poly_positions[0].x - self.poly_contact.height
input_position2 = vector(xoffset - self.poly_contact.width,
yoffset + self.poly_contact.via_layer_position.x)
self.add_layout_pin(text="B",
@ -347,8 +339,8 @@ class nand_2(design.design):
def route_output(self):
""" routing for output Z """
yoffset = (self.nmos1.height - 2 * drc["minwidth_metal1"] / 3 +
(self.height - self.pmos1.height - self.nmos1.height - drc["minwidth_metal1"]) / 2 )
yoffset = (self.nmos.height - 2 * drc["minwidth_metal1"] / 3 +
(self.height - self.pmos.height - self.nmos.height - drc["minwidth_metal1"]) / 2 )
xoffset = self.drain_position.x
offset = vector(xoffset, yoffset)
output_length = self.width - xoffset
@ -360,11 +352,11 @@ class nand_2(design.design):
def extend_wells(self):
""" Extension of well """
middle_point = (self.nmos_position1.y + self.nmos1.pwell_position.y
+ self.nmos1.well_height
+ (self.pmos_position1.y + self.pmos1.nwell_position.y
- self.nmos_position1.y - self.nmos1.pwell_position.y
- self.nmos1.well_height) / 2)
middle_point = (self.nmos_position1.y + self.nmos.pwell_position.y
+ self.nmos.well_height
+ (self.pmos_position1.y + self.pmos.nwell_position.y
- self.nmos_position1.y - self.nmos.pwell_position.y
- self.nmos.well_height) / 2)
offset = self.nwell_position = vector(0, middle_point)
self.nwell_height = self.height - middle_point
self.add_rect(layer="nwell",
@ -389,38 +381,38 @@ class nand_2(design.design):
def extend_active(self):
""" Extension of active region """
self.active_width = (self.pmos1.active_width
self.active_width = (self.pmos.active_width
+ drc["active_to_body_active"]
+ self.pmos1.active_contact.width)
offset = (self.pmos1.active_position
+ self.pmos.active_contact.width)
offset = (self.pmos.active_position
+ self.pmos_position2.scale(1,0)
+ self.pmos_position1.scale(0,1))
self.add_rect(layer="active",
offset=offset,
width=self.active_width,
height=self.pmos1.active_height)
height=self.pmos.active_height)
offset = offset + vector(self.pmos1.active_width, 0)
width = self.active_width - self.pmos1.active_width
offset = offset + vector(self.pmos.active_width, 0)
width = self.active_width - self.pmos.active_width
self.add_rect(layer="nimplant",
offset=offset,
width=width,
height=self.pmos1.active_height)
height=self.pmos.active_height)
offset = vector(self.nmos_position2.x + self.nmos1.active_position.x,
self.nmos_position1.y - self.nmos1.active_height
- self.nmos1.active_position.y + self.nmos1.height)
offset = vector(self.nmos_position2.x + self.nmos.active_position.x,
self.nmos_position1.y - self.nmos.active_height
- self.nmos.active_position.y + self.nmos.height)
self.add_rect(layer="active",
offset=offset,
width=self.active_width,
height=self.nmos1.active_height)
height=self.nmos.active_height)
offset = offset + vector(self.nmos1.active_width,0)
width = self.active_width - self.nmos1.active_width
offset = offset + vector(self.nmos.active_width,0)
width = self.active_width - self.nmos.active_width
self.add_rect(layer="pimplant",
offset=offset,
width=width,
height=self.nmos1.active_height)
height=self.nmos.active_height)
def input_load(self):

View File

@ -63,19 +63,17 @@ class nand_3(design.design):
def create_ptx(self):
""" Create ptx but not yet placed"""
# If full contacts, this will interfere with the C input in SCMOS
self.nmos = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="nmos")
self.add_mod(self.nmos)
self.add_mod(self.nmos)
mults=self.tx_mults,
tx_type="nmos",
num_contacts=1)
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_size,
mults=self.tx_mults,
tx_type="pmos")
self.add_mod(self.pmos)
self.add_mod(self.pmos)
self.add_mod(self.pmos)
def setup_layout_constants(self):
""" setup layout constraints """
@ -183,7 +181,7 @@ class nand_3(design.design):
+ self.pmos.active_width + drc["active_to_body_active"])
yoffset = self.pmos_position1.y + self.pmos.active_contact_positions[0].y
self.nwell_contact_position = vector(xoffset, yoffset)
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_of_tacts))
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_contacts))
xoffset = self.nmos_position3.x + (self.nmos.active_position.x
+ self.nmos.active_width
@ -192,7 +190,7 @@ class nand_3(design.design):
- self.nmos.active_contact_positions[0].y
- self.nmos.active_contact.height)
self.pwell_contact_position = vector(xoffset, yoffset)
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_of_tacts))
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_contacts))
def connect_well_contacts(self):
""" Connect well contacts to vdd and gnd rail """
@ -314,7 +312,6 @@ class nand_3(design.design):
def route_input_gate_A(self):
""" routing for input A """
offset = self.pmos_position1 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height)
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
@ -330,7 +327,6 @@ class nand_3(design.design):
def route_input_gate_B(self):
""" routing for input B """
offset = self.pmos_position2 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+1*(self.m1m2_via.width+drc["metal1_to_metal1"]))
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)
@ -345,7 +341,7 @@ class nand_3(design.design):
def route_input_gate_C(self):
""" routing for input C """
""" routing for input C """
offset = self.pmos_position3 + self.pmos.poly_positions[0] - vector(0,self.poly_contact.height+2*(self.m1m2_via.width+drc["metal1_to_metal1"]))
via_offset = offset + self.poly_contact.first_layer_position.scale(-1,0) - vector(self.poly_contact.first_layer_width,0) + vector(drc["minwidth_poly"],0)

View File

@ -10,7 +10,7 @@ class nor_2(design.design):
"""
This module generates gds of a parametrically sized 2_input nor.
This model use ptx to generate a 2_input nand within a cetrain height.
This model use ptx to generate a 2_input nor within a cetrain height.
The 2_input nor cell_height should be the same as the 6t library cell.
If pmos can not fit in the given vertical space, it will be folded
based so that it takes minmium horiztonal space.
@ -27,6 +27,8 @@ class nor_2(design.design):
design.design.__init__(self, name)
debug.info(2, "create nor_2 structure {0} with size of {1}".format(name, nmos_width))
debug.check(nmos_width==drc["minwidth_tx"], "Need to rewrite nor2 for sizing.")
self.nmos_width = nmos_width
self.height = height
@ -47,9 +49,9 @@ class nor_2(design.design):
# These aren't for instantiating, but we use them to get the dimensions
self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.pmos1.num_of_tacts))
dimensions=(1, self.pmos.num_contacts))
self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.nmos1.num_of_tacts))
dimensions=(1, self.nmos.num_contacts))
self.setup_layout_constants()
self.add_rails()
@ -64,32 +66,22 @@ class nor_2(design.design):
nmos_mults = 1
for pmos_mults in range(1, 5):
nmos_size = self.nmos_width
pmos_size = 4 * self.nmos_width / pmos_mults
pmos_size = 3 * self.nmos_width / pmos_mults
test_nmos = ptx(width=nmos_size,
mults=nmos_mults,
tx_type="nmos")
test_pmos = ptx(width=pmos_size,
mults=pmos_mults,
tx_type="nmos")
# this how the position is done for now
# postion noms and pmos and put A at 0.3 of the margin of it and put B and the 3rd m1 track above it
# if there is no left space then it means the nmos is too big, we
# need to increase the mults number
gate_to_gate = drc["poly_to_poly"] - drc["minwidth_metal1"]
edge_to_nmos = max(drc["metal1_to_metal1"] - test_nmos.active_contact_positions[0].y,
0.5 * gate_to_gate - test_nmos.poly_positions[0].y)
edge_to_pmos = max(drc["metal1_to_metal1"] - test_pmos.active_contact_positions[0].y,
0.5 * gate_to_gate - test_pmos.poly_positions[0].y)
route_margin = 0.5 * (self.poly_contact.second_layer_width
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
pmos_loc = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
- edge_to_pmos - test_pmos.height)
nmos_loc = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
leftspace = (0.7 * (pmos_loc.y - nmos_loc.y)
- 3 * route_margin - 2 * drc["metal1_to_metal1"])
if leftspace >= 0:
# FIXME: This is a hack because the old code sucked and didn't work.
# We will rewrite the entire module soon.
# compute the remaining space roughly including rails and tx heights
track_space = self.height - test_nmos.height + test_pmos.height - drc["metal1_to_metal1"] - drc["minwidth_metal1"]
# 3 contacted track space
if track_space > 3*(self.m1m2_via.height + drc["metal1_to_metal1"]):
break
self.nmos_size = nmos_size
self.pmos_size = pmos_size
@ -98,23 +90,15 @@ class nor_2(design.design):
def create_modules(self):
"""transistors are created as modules"""
self.nmos1 = ptx(width=self.nmos_size,
mults=self.nmos_mults,
tx_type="nmos")
self.add_mod(self.nmos1)
self.nmos2 = ptx(width=self.nmos_size,
mults=self.nmos_mults,
tx_type="nmos")
self.add_mod(self.nmos2)
self.nmos = ptx(width=self.nmos_size,
mults=self.nmos_mults,
tx_type="nmos")
self.add_mod(self.nmos)
self.pmos1 = ptx(width=self.pmos_size,
mults=self.pmos_mults,
tx_type="pmos")
self.add_mod(self.pmos1)
self.pmos2 = ptx(width=self.pmos_size,
mults=self.pmos_mults,
tx_type="pmos")
self.add_mod(self.pmos2)
self.pmos = ptx(width=self.pmos_size,
mults=self.pmos_mults,
tx_type="pmos")
self.add_mod(self.pmos)
def setup_layout_constants(self):
@ -122,30 +106,32 @@ class nor_2(design.design):
# determines the spacing between the edge and nmos (rail to active
# metal or poly_to_poly spacing)
half_gate_to_gate = 0.5 * (drc["poly_to_poly"] - drc["minwidth_metal1"])
edge_to_nmos = max(drc["metal1_to_metal1"] - self.nmos1.active_contact_positions[0].y,
half_gate_to_gate - self.nmos1.poly_positions[0].y)
edge_to_nmos = max(drc["metal1_to_metal1"] - self.nmos.active_contact_positions[0].y,
half_gate_to_gate - self.nmos.poly_positions[0].y)
# determine the position of the first transistor from the left
self.nmos_loc1 = vector(0, 0.5 * drc["minwidth_metal1"] + edge_to_nmos)
offset = self.nmos_loc1 + vector(0,self.nmos1.height)
self.nmos_position1 = vector(0,
0.5 * drc["minwidth_metal1"] + edge_to_nmos)
offset = self.nmos_position1 + vector(0,self.nmos.height)
x = vector(self.nmos2.active_width - self.nmos2.active_contact.width, 0)
self.nmos_loc2 = x + self.nmos_loc1.scale(0,1)
x = vector(self.nmos.active_width - self.nmos.active_contact.width, 0)
self.nmos_position2 = x + self.nmos_position1.scale(0,1)
# determines the spacing between the edge and pmos
edge_to_pmos = max(drc["metal1_to_metal1"] - self.pmos1.active_contact_positions[0].y,
half_gate_to_gate - self.pmos1.poly_positions[0].y)
self.pmos_loc1 = vector(0, self.height - 0.5 * drc["minwidth_metal1"]
- edge_to_pmos - self.pmos1.height)
self.pmos_loc2 = self.pmos_loc1 + vector(self.pmos1.width,0)
edge_to_pmos = max(drc["metal1_to_metal1"] - self.pmos.active_contact_positions[0].y,
half_gate_to_gate - self.pmos.poly_positions[0].y)
self.pmos_position1 = vector(0,
self.height - 0.5 * drc["minwidth_metal1"]
- edge_to_pmos - self.pmos.height)
self.pmos_position2 = self.pmos_position1 + vector(self.pmos.width,0)
self.well_width = max(self.pmos_loc2.x + self.pmos2.active_position.x
+ self.pmos2.active_width
+ drc["active_to_body_active"] + self.nwell_contact.width
+ drc["well_enclosure_active"],
self.nmos_loc2.x + self.nmos2.active_position.x
+ self.nmos2.active_width
+ drc["active_to_body_active"] + drc["well_enclosure_active"])
self.well_width = max(self.pmos_position2.x + self.pmos.active_position.x
+ self.pmos.active_width
+ drc["active_to_body_active"] + self.nwell_contact.width
+ drc["well_enclosure_active"],
self.nmos_position2.x + self.nmos.active_position.x
+ self.nmos.active_width
+ drc["active_to_body_active"] + drc["well_enclosure_active"])
self.width = self.well_width
def add_rails(self):
@ -153,93 +139,92 @@ class nor_2(design.design):
rail_height = drc["minwidth_metal1"]
self.rail_height = rail_height
# Relocate the origin
self.gnd_loc = vector(0, - 0.5 * drc["minwidth_metal1"])
self.gnd_position = vector(0, - 0.5 * drc["minwidth_metal1"])
self.add_layout_pin(text="gnd",
layer="metal1",
offset=self.gnd_loc,
offset=self.gnd_position,
width=rail_width,
height=rail_height)
self.vdd_loc = self.gnd_loc + vector(0, self.height)
self.vdd_position = self.gnd_position + vector(0, self.height)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=self.vdd_loc,
width=rail_width,
height=rail_height)
layer="metal1",
offset=self.vdd_position,
width=rail_width,
height=rail_height)
def add_ptx(self):
""" transistors are placed in the layout"""
offset = self.nmos_loc1 + vector(0, self.nmos1.height)
offset = self.nmos_position1 + vector(0, self.nmos.height)
self.add_inst(name="nmos1",
mod=self.nmos1,
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["Z", "A", "gnd", "gnd"])
offset = self.nmos_loc2 + vector(0, self.nmos2.height)
offset = self.nmos_position2 + vector(0, self.nmos.height)
self.add_inst(name="nmos2",
mod=self.nmos2,
mod=self.nmos,
offset=offset,
mirror="MX")
self.connect_inst(["Z", "B", "gnd", "gnd"])
offset = self.pmos_loc1
offset = self.pmos_position1
self.add_inst(name="pmos1",
mod=self.pmos1,
mod=self.pmos,
offset=offset)
self.connect_inst(["vdd", "A", "net1", "vdd"])
offset = self.pmos_loc2
offset = self.pmos_position2
self.add_inst(name="pmos2",
mod=self.pmos2,
mod=self.pmos,
offset=offset)
self.connect_inst(["net1", "B", "Z", "vdd"])
def add_well_contacts(self):
layer_stack = ("active", "contact", "metal1")
xoffset = (self.pmos_loc2.x + self.pmos1.active_position.x
+ self.pmos1.active_width + drc["active_to_body_active"])
yoffset = (self.pmos_loc1.y
+ self.pmos1.active_contact_positions[0].y)
self.nwell_contact_loc = vector(xoffset, yoffset)
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_loc,(1,self.pmos1.num_of_tacts))
xoffset = (self.pmos_position2.x + self.pmos.active_position.x
+ self.pmos.active_width + drc["active_to_body_active"])
yoffset = (self.pmos_position1.y
+ self.pmos.active_contact_positions[0].y)
self.nwell_contact_position = vector(xoffset, yoffset)
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_contacts))
xoffset = self.nmos_loc2.x + (self.nmos1.active_position.x
+ self.nmos1.active_width
+ drc["active_to_body_active"])
yoffset = (self.nmos_loc1.y + self.nmos1.height
- self.nmos1.active_contact_positions[0].y
- self.nmos1.active_contact.height)
self.pwell_contact_loc = vector(xoffset, yoffset)
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_loc,(1,self.nmos1.num_of_tacts))
xoffset = self.nmos_position2.x + (self.nmos.active_position.x
+ self.nmos.active_width
+ drc["active_to_body_active"])
yoffset = (self.nmos_position1.y + self.nmos.height
- self.nmos.active_contact_positions[0].y
- self.nmos.active_contact.height)
self.pwell_contact_position = vector(xoffset, yoffset)
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_contacts))
def route(self):
self.route_pins()
self.connect_well_contacts()
M1_track = (self.B_loc.y + drc["metal1_to_metal1"]
+ .5 * (self.poly_contact.second_layer_width
+ drc["minwidth_metal1"]))
M1_track = self.B_position.y + max(drc["minwidth_metal2"], self.poly_contact.second_layer_width) + drc["metal2_to_metal2"]
self.connect_tx(M1_track)
self.connect_poly()
def connect_well_contacts(self):
""" Connect well contacts to vdd and gnd rail """
well_tap_length = self.height - self.nwell_contact_loc.y
xoffset = (self.nwell_contact_loc.x
+ self.nwell_contact.second_layer_position.x
- self.nwell_contact.first_layer_position.x)
offset = [xoffset, self.nwell_contact_loc.y]
well_tap_length = self.height - self.nwell_contact_position.y
xoffset = (self.nwell_contact_position.x
+ self.nwell_contact.second_layer_position.x
- self.nwell_contact.first_layer_position.x)
offset = [xoffset, self.nwell_contact_position.y]
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
height=well_tap_length)
offset = (self.pwell_contact_loc.scale(1,0)
+ self.pwell_contact.second_layer_position.scale(1,0)
- self.pwell_contact.first_layer_position.scale(1,0))
well_tap_length = self.pwell_contact_loc.y
offset = (self.pwell_contact_position.scale(1,0)
+ self.pwell_contact.second_layer_position.scale(1,0)
- self.pwell_contact.first_layer_position.scale(1,0))
well_tap_length = self.pwell_contact_position.y
self.add_rect(layer="metal1",
offset=offset,
width=drc["minwidth_metal1"],
@ -248,49 +233,49 @@ class nor_2(design.design):
def connect_tx(self, M1_track):
"""Connect transistor pmos drains to vdd and nmos drains to gnd rail"""
# the first pmos drain to Vdd
for i in range(len(self.pmos1.active_contact_positions)):
contact_pos = self.pmos_loc1 + self.pmos1.active_contact_positions[i]
for i in range(len(self.pmos.active_contact_positions)):
contact_pos = self.pmos_position1 + self.pmos.active_contact_positions[i]
if i % 2 == 0:
correct = self.pmos1.active_contact.second_layer_position.scale(1,0)
correct = self.pmos.active_contact.second_layer_position.scale(1,0)
drain_posistion = contact_pos + correct
height = self.vdd_loc.y - drain_posistion.y
height = self.vdd_position.y - drain_posistion.y
self.add_rect(layer="metal1",
offset=drain_posistion,
width=drc["minwidth_metal1"],
height=height)
else:
# source to pmos2
correct = (self.pmos1.active_contact.second_layer_position.scale(1,0)
+ vector(self.pmos1.active_contact.second_layer_width,
0).scale(.5,0))
source_loc = contact_pos + correct
mid = [self.pmos_loc2.x, M1_track]
self.add_path("metal1", [source_loc, mid])
correct = (self.pmos.active_contact.second_layer_position.scale(1,0)
+ vector(self.pmos.active_contact.second_layer_width,
0).scale(0.5,0))
source_position = contact_pos + correct
mid = [self.pmos_position2.x, M1_track]
self.add_path("metal1", [source_position, mid])
# the second pmos
for i in range(len(self.pmos2.active_contact_positions)):
for i in range(len(self.pmos.active_contact_positions)):
if i % 2 == 0:
# source to pmos2
pmos_active =self.pmos_loc2+self.pmos2.active_contact_positions[i]
correct= (self.pmos2.active_contact.second_layer_position.scale(1,0)
+ vector(0.5 * self.pmos2.active_contact.second_layer_width,0))
source_loc = pmos_active + correct
mid = [self.pmos_loc2.x, M1_track]
self.add_path("metal1", [source_loc, mid])
pmos_active =self.pmos_position2+self.pmos.active_contact_positions[i]
correct= (self.pmos.active_contact.second_layer_position.scale(1,0)
+ vector(0.5 * self.pmos.active_contact.second_layer_width,0))
source_position = pmos_active + correct
mid = [self.pmos_position2.x, M1_track]
self.add_path("metal1", [source_position, mid])
# two nmos source to gnd
source_posistion1 = (self.nmos_loc1
+ self.nmos1.active_contact_positions[0]
+ self.nmos1.active_contact.second_layer_position.scale(1,0))
height = self.gnd_loc.y - source_posistion1.y
source_posistion1 = (self.nmos_position1
+ self.nmos.active_contact_positions[0]
+ self.nmos.active_contact.second_layer_position.scale(1,0))
height = self.gnd_position.y - source_posistion1.y
self.add_rect(layer="metal1",
offset=source_posistion1,
width=drc["minwidth_metal1"],
height=height)
source_posistion2 = (self.nmos_loc2
+ self.nmos2.active_contact_positions[1]
+ self.nmos2.active_contact.second_layer_position.scale(1,0))
height = self.gnd_loc.y - source_posistion2.y
source_posistion2 = (self.nmos_position2
+ self.nmos.active_contact_positions[1]
+ self.nmos.active_contact.second_layer_position.scale(1,0))
height = self.gnd_position.y - source_posistion2.y
self.add_rect(layer="metal1",
offset=source_posistion2,
width=drc["minwidth_metal1"],
@ -299,29 +284,29 @@ class nor_2(design.design):
def connect_poly(self):
"""connect connect poly between nmos and pmos"""
# connect pmos1 poly
nmos_gate = (self.nmos_loc1
+ self.nmos1.poly_positions[0]
nmos_gate = (self.nmos_position1
+ self.nmos.poly_positions[0]
+ vector(0.5 * drc["minwidth_poly"], 0))
for i in range(len(self.pmos1.poly_positions)):
pmos_gate = (self.pmos_loc1
+ self.pmos1.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0))
for i in range(len(self.pmos.poly_positions)):
pmos_gate = (self.pmos_position1
+ self.pmos.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0))
mid1 = [pmos_gate.x, pmos_gate.y - drc["poly_to_active"]]
self.add_path("poly", [nmos_gate, mid1, pmos_gate])
# connect pmos2 poly
nmos_gate = vector(self.nmos_loc2[0]
+ self.nmos2.poly_positions[0].x
+ 0.5 * drc["minwidth_poly"],
self.nmos_loc1.y
+ self.nmos1.poly_positions[0].y)
for i in range(len(self.pmos2.poly_positions)):
pmos_gate = (self.pmos_loc2
+ self.pmos2.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0))
nmos_gate = vector(self.nmos_position2[0]
+ self.nmos.poly_positions[0].x
+ 0.5 * drc["minwidth_poly"],
self.nmos_position1.y
+ self.nmos.poly_positions[0].y)
for i in range(len(self.pmos.poly_positions)):
pmos_gate = (self.pmos_position2
+ self.pmos.poly_positions[i]
+ vector(0.5 * drc["minwidth_poly"], 0))
mid1 = vector(pmos_gate.x,
nmos_gate.y + self.nmos2.height
+ drc["poly_to_active"])
nmos_gate.y + self.nmos.height
+ drc["poly_to_active"])
self.add_path("poly", [nmos_gate, mid1, pmos_gate])
def route_pins(self):
@ -334,40 +319,40 @@ class nor_2(design.design):
def route_input_A(self):
"""create input A layout"""
xoffset = self.nmos1.poly_positions[0].x
yoffset = self.nmos_loc1.y + self.nmos1.height \
+ 0.3 * (self.pmos_loc1.y - self.nmos_loc1.y \
- self.nmos1.height)
self.A_loc = vector(xoffset, yoffset)
xoffset = self.nmos.poly_positions[0].x
# HACK: added 1.5, since we're going to rewrite this.
yoffset = self.nmos_position1.y + drc["well_enclosure_active"] + self.nmos.active_height + 1.5*self.poly_contact.height
self.A_position = vector(xoffset, yoffset)
# gate input
offset = self.A_loc - vector(0, self.poly_contact.width)
offset = self.A_position - vector(0, 0.5 * self.poly_contact.width)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
# connect gate input to tx gate
offset = self.A_loc - vector(self.poly_contact.first_layer_position.y,
self.poly_contact.width)
offset = self.A_position - vector(self.poly_contact.first_layer_position.y,
0.5 * self.poly_contact.width)
self.add_rect(layer="poly",
offset=offset,
width=self.poly_contact.first_layer_position.y + drc["minwidth_poly"],
height=self.poly_contact.first_layer_width)
# extend the metal to the boundary of the cell
input_length = self.A_loc.x
offset = [0, self.A_loc.y - drc["minwidth_metal1"]]
input_length = self.A_position.x
offset = [0, self.A_position.y - 0.5 * drc["minwidth_metal1"]]
self.add_layout_pin(text="A",
layer="metal1",
offset=offset,
width=input_length,
height=drc["minwidth_metal1"])
layer="metal1",
offset=offset,
width=input_length,
height=drc["minwidth_metal1"])
def route_input_B(self):
"""create input B layout """
xoffset = self.pmos2.poly_positions[0].x + self.pmos_loc2.x
yoffset = self.A_loc.y + 0.5 * (self.poly_contact.second_layer_width \
+ drc["minwidth_metal1"]) + drc["metal1_to_metal1"]
self.B_loc = vector(xoffset, yoffset)
offset = self.B_loc - vector(0, 0.5 * self.poly_contact.width)
xoffset = self.pmos.poly_positions[0].x \
+ self.pmos_position2.x
yoffset = self.A_position.y \
+ max(drc["minwidth_metal2"], self.poly_contact.second_layer_width) + drc["metal2_to_metal2"]
self.B_position = vector(xoffset, yoffset)
offset = self.B_position - vector(0, 0.5 * self.poly_contact.width)
self.add_contact(layers=("poly", "contact", "metal1"),
offset=offset,
rotate=90)
@ -377,53 +362,55 @@ class nor_2(design.design):
width=-(self.poly_contact.first_layer_position.y + drc["minwidth_poly"]),
height=self.poly_contact.first_layer_width)
self.add_layout_pin(text="B",
layer="metal1",
offset=[0, self.B_loc.y - 0.5 * drc["minwidth_metal1"]],
width=self.B_loc.x,
height=drc["minwidth_metal1"])
layer="metal1",
offset=[0,
self.B_position.y - 0.5 * drc["minwidth_metal1"]],
width=self.B_position.x,
height=drc["minwidth_metal1"])
def route_output(self):
"""route the output to nmos pmos """
self.Z_loc = vector(self.width, self.A_loc.y)
self.Z_position = vector(self.width, self.A_position.y)
# route nmos drain to Z
nmos_contact = (self.nmos_loc1
+ self.nmos1.active_contact_positions[1]
+ self.nmos1.active_contact.second_layer_position
+ vector(self.nmos1.active_contact.second_layer_width,0).scale(0.5, 0))
mid = [nmos_contact.x, self.A_loc.y]
self.add_path("metal1", [self.Z_loc, mid, nmos_contact])
for i in range(len(self.pmos2.poly_positions) + 1):
nmos_contact = (self.nmos_position1
+ self.nmos.active_contact_positions[1]
+ self.nmos.active_contact.second_layer_position
+ vector(self.nmos.active_contact.second_layer_width,
0).scale(0.5, 0))
mid = [nmos_contact.x, self.A_position.y]
self.add_path("metal1", [self.Z_position, mid, nmos_contact])
for i in range(len(self.pmos.poly_positions) + 1):
if i % 2 == 1:
# pmos2 drain to Z
pmos_contact = (self.pmos_loc2
+ self.pmos1.active_contact_positions[i]
+ self.pmos2.active_contact.second_layer_position.scale(1, 0)
+ vector(self.pmos2.active_contact.second_layer_width,0).scale(0.5, 0))
pmos_contact = (self.pmos_position2
+ self.pmos.active_contact_positions[i]
+ self.pmos.active_contact.second_layer_position.scale(1, 0)
+ vector(self.pmos.active_contact.second_layer_width,
0).scale(0.5, 0))
offset = pmos_contact - vector(0.5 * self.m1m2_via.width, 0)
self.add_via(layers=("metal1", "via1", "metal2"),
offset=offset)
mid = [pmos_contact.x, self.Z_loc.y]
mid = [pmos_contact.x, self.Z_position.y]
self.add_wire(("metal1", "via1", "metal2"),
[self.Z_loc, mid, pmos_contact])
[self.Z_position, mid, pmos_contact])
self.add_layout_pin(text="Z",
layer="metal1",
offset=mid - vector(0,0.5*drc["minwidth_metal1"]),
width=self.Z_loc.x-mid[0],
width=self.Z_position.x-mid[0],
height=drc["minwidth_metal1"])
def extend_wells(self):
""" extend well for well contact"""
middle_point = (self.nmos_loc1.y
+ self.nmos1.pwell_position.y
+ self.nmos1.well_height
+ (self.pmos_loc1.y
+ self.pmos1.nwell_position.y
- self.nmos_loc1.y
- self.nmos1.pwell_position.y
- self.nmos1.well_height) / 2 )
middle_point = (self.nmos_position1.y
+ self.nmos.pwell_position.y
+ self.nmos.well_height
+ (self.pmos_position1.y
+ self.pmos.nwell_position.y
- self.nmos_position1.y
- self.nmos.pwell_position.y
- self.nmos.well_height) / 2 )
self.nwell_position = vector(0, middle_point)
self.nwell_height = self.height - middle_point
self.add_rect(layer="nwell",
@ -448,34 +435,34 @@ class nor_2(design.design):
def extend_active(self):
""" extend active for well contact"""
self.active_width = self.pmos1.active_width \
+ drc["active_to_body_active"] \
+ self.pmos1.active_contact.width
offset = (self.pmos_loc2.scale(1,0)
+ self.pmos_loc1.scale(0,1)
+ self.pmos1.active_position)
self.active_width = self.pmos.active_width \
+ drc["active_to_body_active"] \
+ self.pmos.active_contact.width
offset = (self.pmos_position2.scale(1,0)
+ self.pmos_position1.scale(0,1)
+ self.pmos.active_position)
self.add_rect(layer="active",
offset=offset,
width=self.active_width,
height=self.pmos1.active_height)
offset = offset + vector(self.pmos1.active_width, 0)
width = self.active_width - self.pmos1.active_width
height=self.pmos.active_height)
offset = offset + vector(self.pmos.active_width, 0)
width = self.active_width - self.pmos.active_width
self.add_rect(layer="nimplant",
offset=offset,
width=width,
height=self.pmos1.active_height)
height=self.pmos.active_height)
offset = (self.nmos1.active_position.scale(1,-1)
+ self.nmos_loc2.scale(1,0)
+ self.nmos_loc1.scale(0,1)
+ vector(0, self.nmos1.height - self.nmos1.active_height))
offset = (self.nmos.active_position.scale(1,-1)
+ self.nmos_position2.scale(1,0)
+ self.nmos_position1.scale(0,1)
+ vector(0, self.nmos.height - self.nmos.active_height))
self.add_rect(layer="active",
offset=offset,
width=self.active_width,
height=self.nmos1.active_height)
offset = offset + vector(self.nmos1.active_width,0)
width = self.active_width - self.nmos1.active_width
height=self.nmos.active_height)
offset = offset + vector(self.nmos.active_width,0)
width = self.active_width - self.nmos.active_width
self.add_rect(layer="pimplant",
offset=offset,
width=width,
height=self.nmos1.active_height)
height=self.nmos.active_height)

View File

@ -31,7 +31,7 @@ class pin_layout:
def __eq__(self, other):
""" Check if these are the same pins for duplicate checks """
if isinstance(other, self.__class__):
return (self.layer==other.layer and self.rect == other.rect)
return (self.name==other.name and self.layer==other.layer and self.rect == other.rect)
else:
return False

View File

@ -53,9 +53,9 @@ class pinv(design.design):
# These aren't for instantiating, but we use them to get the dimensions
self.nwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.pmos.num_of_tacts))
dimensions=(1, self.pmos.num_contacts))
self.pwell_contact = contact.contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.nmos.num_of_tacts))
dimensions=(1, self.nmos.num_contacts))
self.extend_wells()
self.extend_active()
@ -96,15 +96,15 @@ class pinv(design.design):
"""Intiializes a ptx object"""
self.nmos = ptx(width=self.nmos_size,
mults=self.tx_mults,
tx_type="nmos")
self.nmos.connect_fingered_poly()
self.nmos.connect_fingered_active()
tx_type="nmos",
connect_active=True,
connect_poly=True)
self.add_mod(self.nmos)
self.pmos = ptx(width=self.pmos_size,
mults=self.tx_mults,
tx_type="pmos")
self.pmos.connect_fingered_poly()
self.pmos.connect_fingered_active()
tx_type="pmos",
connect_active=True,
connect_poly=True)
self.add_mod(self.pmos)
def setup_layout_constants(self):
@ -333,14 +333,14 @@ class pinv(design.design):
- self.nwell_contact.width,
self.pmos.active_contact_positions[0].y)
self.nwell_contact_position = self.pmos_position + well_contact_offset
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_of_tacts))
self.nwell_contact=self.add_contact(layer_stack,self.nwell_contact_position,(1,self.pmos.num_contacts))
well_contact_offset = vector(self.nmos.active_position.x
+ self.active_width
- self.pwell_contact.width,
self.nmos.active_contact_positions[0].y)
self.pwell_contact_position = self.nmos_position + well_contact_offset
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_of_tacts))
self.pwell_contact=self.add_contact(layer_stack,self.pwell_contact_position,(1,self.nmos.num_contacts))
def connect_well_contacts(self):
"""Connects the well taps to its respective power rails"""

View File

@ -55,8 +55,8 @@ class precharge(design.design):
self.temp_pmos = ptx(width=self.beta * self.ptx_width,
mults=2,
tx_type="pmos")
self.temp_pmos.remove_source_connect()
self.temp_pmos.remove_drain_connect()
#self.temp_pmos.remove_source_connect()
#self.temp_pmos.remove_drain_connect()
def create_contacts(self):
"""Initializes all required contacts/vias for this module"""

View File

@ -11,16 +11,25 @@ class ptx(design.design):
Creates a simple MOS transistor. poly_positions are the ll of the poly gate. active_contact_positions
is an array of the positions of the ll of active contacts (left to right)
"""
def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos"):
def __init__(self, width=drc["minwidth_tx"], mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None):
name = "{0}_m{1}_w{2}".format(tx_type, mults, width)
# remove periods for newer spice compatibility
name=re.sub('\.','_',name)
if connect_active:
name += "_a"
if connect_poly:
name += "_p"
if num_contacts:
name += "_c{}".format(num_contacts)
name=re.sub('\.','_',name) # remove periods for newer spice compatibility
design.design.__init__(self, name)
debug.info(3, "create ptx structure {0}".format(name))
self.tx_type = tx_type
self.mults = mults
self.gate_width = width
self.connect_active = connect_active
self.connect_poly = connect_poly
self.num_contacts = num_contacts
self.add_pins()
self.create_layout()
@ -34,21 +43,25 @@ class ptx(design.design):
def create_layout(self):
self.setup_layout_constants()
if self.num_contacts==None:
self.num_contacts=self.calculate_num_contacts()
# This is not actually instantiated but used for calculations
self.num_of_tacts = self.calculate_num_of_tacts()
self.active_contact = contact(layer_stack=("active", "contact", "metal1"),
dimensions=(1, self.num_of_tacts))
dimensions=(1, self.num_contacts))
self.add_active()
self.add_implants()
self.add_poly()
self.add_active_contacts()
# rather than connect these, we let the user of the ptx
# decide to call them.
# self.determine_active_wire_location()
# self.connect_fingered_active()
# self.connect_fingered_poly()
# offset this BEFORE we add the active/poly connections
self.offset_all_coordinates()
if self.connect_active:
self.connect_fingered_active()
if self.connect_poly:
self.connect_fingered_poly()
def offset_attributes(self, coordinate):
"""Translates all stored 2d cartesian coordinates within the
@ -127,6 +140,7 @@ class ptx(design.design):
self.well_height = max(self.gate_width + 2 * (drc["well_enclosure_active"]),
drc["minwidth_well"])
def connect_fingered_poly(self):
poly_connect_length = self.poly_positions[-1].x + self.poly_width \
- self.poly_positions[0].x
@ -138,6 +152,10 @@ class ptx(design.design):
height=drc["minwidth_poly"])
self.poly_connect_index = len(self.objs) - 1
# change the name so it is unique and specifies drain/srouce (p) routed
self.name = self.name + "_p"
def pairwise(self, iterable):
#"s -> (s0,s1), (s1,s2), (s2, s3), ..."
from itertools import tee, izip
@ -192,7 +210,7 @@ class ptx(design.design):
if self.drain_positions:
self.add_path(("metal1"), self.drain_positions)
self.drain_connect_index = len(self.insts) - 1
def add_poly(self):
# left_most poly
poly_xoffset = self.active_contact.via_layer_position.x \
@ -258,14 +276,13 @@ class ptx(design.design):
width=xlength,
height=ylength)
def calculate_num_of_tacts(self):
def calculate_num_contacts(self):
""" Calculates the possible number of source/drain contacts in a column """
possible_length = self.active_height \
- 2 * drc["active_extend_contact"]
# Used for over-riding the contact dimensions
possible_length = self.active_height - 2 * drc["active_extend_contact"]
y = 1
while True:
temp_length = (y * drc["minwidth_contact"]) \
+ ((y - 1) * drc["contact_to_contact"])
temp_length = (y * drc["minwidth_contact"]) + ((y - 1) * drc["contact_to_contact"])
if round(possible_length - temp_length, 6) < 0:
return y - 1
y += 1
@ -279,7 +296,7 @@ class ptx(design.design):
offset = vector(contact_xoffset, contact_yoffset)
self.add_contact(layers=("active", "contact", "metal1"),
offset=offset,
size=(1, self.num_of_tacts))
size=(1, self.num_contacts))
self.active_contact_positions.append(offset)
# middle contact columns
@ -291,7 +308,7 @@ class ptx(design.design):
offset = vector(contact_xoffset, contact_yoffset)
self.add_contact(layers=("active", "contact", "metal1"),
offset=offset,
size=(1, self.num_of_tacts))
size=(1, self.num_contacts))
self.active_contact_positions.append(offset)
@ -302,40 +319,42 @@ class ptx(design.design):
offset = vector(contact_xoffset, contact_yoffset)
self.add_contact(layers=("active", "contact", "metal1"),
offset=offset,
size=(1, self.num_of_tacts))
size=(1, self.num_contacts))
self.active_contact_positions.append(offset)
def remove_drain_connect(self):
# FIXME: This is horrible exception handling!
try:
del self.insts[self.drain_connect_index]
del self.drain_connect_index
self.offset_all_coordinates()
# change the name so it is unique
self.name = self.name + "_rd"
except:
pass
# def remove_drain_connect(self):
# debug.info(3,"Removing drain on {}".format(self.name))
# # FIXME: This is horrible exception handling!
# try:
# del self.insts[self.drain_connect_index]
# del self.drain_connect_index
# self.offset_all_coordinates()
# # change the name so it is unique
# self.name = self.name + "_rd"
# except:
# pass
def remove_source_connect(self):
# FIXME: This is horrible exception handling!
try:
del self.insts[self.source_connect_index]
del self.source_connect_index
if isinstance(self.drain_connect_index, int):
self.drain_connect_index -= 1
self.offset_all_coordinates()
# change the name so it is unique
self.name = self.name + "_rs"
except:
pass
# def remove_source_connect(self):
# debug.info(3,"Removing source on {}".format(self.name))
# # FIXME: This is horrible exception handling!
# try:
# del self.insts[self.source_connect_index]
# del self.source_connect_index
# if isinstance(self.drain_connect_index, int):
# self.drain_connect_index -= 1
# self.offset_all_coordinates()
# # change the name so it is unique
# self.name = self.name + "_rs"
# except:
# pass
def remove_poly_connect(self):
# FIXME: This is horrible exception handling!
try:
del self.objs[self.poly_connect_index]
self.offset_all_coordinates()
# change the name so it is unique
self.name = self.name + "_rp"
except:
pass
# def remove_poly_connect(self):
# # FIXME: This is horrible exception handling!
# try:
# del self.objs[self.poly_connect_index]
# self.offset_all_coordinates()
# # change the name so it is unique
# self.name = self.name + "_rp"
# except:
# pass

View File

@ -72,7 +72,7 @@ class replica_bitline(design.design):
self.rbl_offset = self.bitcell_offset
self.height = self.rbl_offset.y + self.rbl.height
self.height = self.rbl_offset.y + self.rbl.height + self.m2_pitch
self.width = self.rbl_offset.x + self.bitcell.width
@ -256,7 +256,7 @@ class replica_bitline(design.design):
self.add_rect(layer="metal2",
offset=gnd_start,
width=drc["minwidth_metal2"],
height=self.rbl.height+self.bitcell.height+self.inv.width)
height=self.rbl.height+self.bitcell.height+self.inv.width+self.m2_pitch)
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_start.scale(1,0),
@ -296,8 +296,8 @@ class replica_bitline(design.design):
gnd_pin = pin
gnd_end = gnd_pin.uc()
# Add a couple midpoints so that the wire will drop a via and then route horizontal on M1
gnd_mid1 = gnd_start + vector(0,2*drc["metal2_to_metal2"])
gnd_mid2 = gnd_end + vector(0,2*drc["metal2_to_metal2"])
gnd_mid1 = gnd_start + vector(0,self.m2_pitch)
gnd_mid2 = gnd_end + vector(0,self.m2_pitch)
self.add_wire(("metal1","via1","metal2"), [gnd_start, gnd_mid1, gnd_mid2, gnd_end])

View File

@ -11,7 +11,7 @@ class sense_amp(design.design):
Sense amplifier to read a pair of bit-lines.
"""
pin_names = ["BL", "BR", "Dout", "SCLK", "vdd", "gnd"]
pin_names = ["bl", "br", "dout", "en", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("sense_amp", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "sense_amp", GDS["unit"], layer["boundary"])

View File

@ -51,9 +51,9 @@ class sense_amp_array(design.design):
def add_sense_amp(self):
bl_pin = self.amp.get_pin("BL")
br_pin = self.amp.get_pin("BR")
dout_pin = self.amp.get_pin("Dout")
bl_pin = self.amp.get_pin("bl")
br_pin = self.amp.get_pin("br")
dout_pin = self.amp.get_pin("dout")
for i in range(0,self.row_size,self.words_per_row):
@ -83,7 +83,7 @@ class sense_amp_array(design.design):
height=br_pin.height())
self.add_layout_pin(text="data[{0}]".format(i/self.words_per_row),
layer="metal2",
layer="metal3",
offset=dout_offset,
width=dout_pin.width(),
height=dout_pin.height())
@ -110,7 +110,7 @@ class sense_amp_array(design.design):
height=drc["minwidth_metal1"])
# add sclk rail across entire array
sclk_offset = self.amp.get_pin("SCLK").ll().scale(0,1)
sclk_offset = self.amp.get_pin("en").ll().scale(0,1)
self.add_layout_pin(text="en",
layer="metal1",
offset=sclk_offset,

View File

@ -28,6 +28,10 @@ class sram(design.design):
c = reload(__import__(OPTS.config.bitcell))
self.mod_bitcell = getattr(c, OPTS.config.bitcell)
self.bitcell = self.mod_bitcell()
c = reload(__import__(OPTS.config.ms_flop))
self.mod_ms_flop = getattr(c, OPTS.config.ms_flop)
self.ms_flop = self.mod_ms_flop()
# reset the static duplicate name checker for unit tests
@ -46,13 +50,21 @@ class sram(design.design):
# These aren't for instantiating, but we use them to get the dimensions
self.poly_contact = contact(layer_stack=("poly", "contact", "metal1"))
self.poly_contact_offset = vector(0.5*self.poly_contact.width,0.5*self.poly_contact.height)
self.m1m2_via = contact(layer_stack=("metal1", "via1", "metal2"))
self.m2m3_via = contact(layer_stack=("metal2", "via2", "metal3"))
# M1/M2 routing pitch is based on contacted pitch
# For different layer width vias
self.m2m3_offset_fix = vector(0,0.5*(drc["minwidth_metal3"]-drc["minwidth_metal2"]))
self.m1_width = drc["minwidth_metal1"]
self.m2_width = drc["minwidth_metal2"]
self.m3_width = drc["minwidth_metal3"]
# M1/M2 routing pitch is based on contacted pitch of the biggest layer
self.m1_pitch = max(self.m1m2_via.width,self.m1m2_via.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.m3_pitch = max(self.m2m3_via.width,self.m2m3_via.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
self.control_size = 6
self.bank_to_bus_distance = 5*drc["minwidth_metal3"]
@ -60,6 +72,10 @@ class sram(design.design):
self.compute_sizes()
self.add_pins()
self.create_layout()
# Can remove the following, but it helps for debug!
self.add_lvs_correspondence_points()
self.DRC_LVS()
def compute_sizes(self):
@ -126,7 +142,11 @@ class sram(design.design):
self.add_pin("DATA[{0}]".format(i))
for i in range(self.addr_size):
self.add_pin("ADDR[{0}]".format(i))
for pin in ["CSb","WEb","OEb","clk","vdd","gnd"]:
self.control_logic_inputs=["CSb", "WEb", "OEb", "clk"]
self.control_logic_outputs=["s_en", "w_en", "tri_en", "tri_en_bar", "clk_bar", "clk_buf"]
for pin in self.control_logic_inputs+["vdd","gnd"]:
self.add_pin(pin)
def create_layout(self):
@ -138,32 +158,358 @@ class sram(design.design):
self.add_single_bank_modules()
self.add_single_bank_pins()
self.route_single_bank()
elif self.num_banks == 2:
self.add_two_bank_modules()
self.route_two_banks()
else:
self.add_multi_bank_modules()
self.route_top_banks()
self.add_multi_bank_pins()
return
self.route_2or4_banks()
if self.num_banks > 2:
self.route_bottom_banks()
self.route_4_banks()
self.route_bank_and_control()
self.route_supplies()
def add_two_bank_modules(self):
""" Adds the modules and the buses to the top level """
########################
# First, compute all of the offsets
########################
self.num_vertical_line = self.bank_addr_size + self.control_size + self.num_banks + 1
self.num_horizontal_line = self.word_size
self.vertical_bus_width = self.m2_pitch*self.num_vertical_line
self.data_bus_height = self.m3_pitch*self.num_horizontal_line
self.control_bus_height = self.m1_pitch*(self.control_size+2)
self.supply_bus_height = self.m1_pitch*2 # 2 for vdd/gnd placed with control bus
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width, "Bank is too small compared to control logic.")
# This depends on the bus widths, but nothing else
self.add_top_banks()
self.vertical_bus_height = self.bank.height + self.bank_to_bus_distance + self.data_bus_height + self.control_bus_height
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 2*self.power_rail_pitch)
self.data_bus_width = 2*(self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
self.data_bus_offset = vector(0, 2*self.power_rail_pitch + self.bank.height + self.bank_to_bus_distance)
self.supply_bus_width = self.data_bus_width
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height)
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
# The address bus extends down through the power rails, but control and bank_sel bus don't
self.addr_bus_height = self.vertical_bus_height + 2*self.power_rail_pitch
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
# Control is placed at the top above the control bus and everything
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
# Bank select flops get put to the right of control logic above bank1 and the buses
# Leave a pitch to get the vdd rails up to M2
self.msb_address_position = vector(max(self.bank_inst[1].lx(),self.control_logic.width),
self.supply_bus_offset.y+self.supply_bus_height + 2*self.m1_pitch + self.msb_address.width)
########################
# Second, place the buses
########################
# Vertical bus
# The order of the control signals on the control bus:
self.control_bus_names = ["clk_buf", "tri_en_bar", "tri_en", "clk_bar", "w_en", "s_en"]
self.vert_control_bus_positions = self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
names=self.control_bus_names,
length=self.vertical_bus_height,
vertical=True)
self.addr_bus_names=[]
for i in range(self.addr_size):
self.addr_bus_names.append("ADDR[{}]".format(i))
self.vert_control_bus_positions.update(self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.addr_bus_offset,
names=self.addr_bus_names,
length=self.addr_bus_height,
vertical=True,
make_pins=True))
self.bank_sel_bus_names = ["bank_sel[0]","bank_sel[1]"]
self.vert_control_bus_positions.update(self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names,
length=self.vertical_bus_height,
vertical=True))
# Horizontal data bus
self.data_bus_names = []
for i in range(0, self.word_size):
self.data_bus_names.append("DATA[{}]".format(i))
self.data_bus_positions = self.create_bus(layer="metal3",
pitch=self.m3_pitch,
offset=self.data_bus_offset,
names=self.data_bus_names,
length=self.data_bus_width,
vertical=False,
make_pins=True)
# Horizontal control logic bus
# vdd/gnd in bus go along whole SRAM
# FIXME: Fatten these wires?
self.horz_control_bus_positions = self.create_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset,
names=["vdd", "gnd"],
length=self.supply_bus_width,
vertical=False)
self.horz_control_bus_positions.update(self.create_bus(layer="metal1",
pitch=self.m1_pitch,
offset=self.control_bus_offset,
names=self.control_bus_names,
length=self.control_bus_width,
vertical=False))
########################
# Third, place the other logic (banks are done first to anchor the design)
########################
self.add_control_logic(position=self.control_logic_position, rotate=0)
self.msb_address_inst = self.add_inst(name="msb_address",
mod=self.msb_address,
offset=self.msb_address_position,
rotate=270)
self.msb_bank_sel_addr = "ADDR[{}]".format(self.addr_size-1)
self.connect_inst([self.msb_bank_sel_addr,"bank_sel[1]","bank_sel[0]","clk_buf", "vdd", "gnd"])
self.width= self.data_bus_width
self.height = self.control_logic_inst.uy()
def route_two_banks(self):
""" Route all of the signals for the two bank SRAM. """
# create the input control pins
for n in self.control_logic_inputs:
self.copy_layout_pin(self.control_logic_inst, n.lower(), n)
# connect the control logic to the control bus
for n in self.control_logic_outputs + ["vdd", "gnd"]:
pins = self.control_logic_inst.get_pins(n)
for pin in pins:
if pin.layer=="metal2":
pin_pos = pin.bc()
break
rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_center_via(("metal1","via1","metal2"),rail_pos)
# connect the control logic cross bar
for n in self.control_logic_outputs:
cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y)
self.add_center_via(("metal1","via1","metal2"),cross_pos)
# connect the horizontal control bus to the vertical bus
# connect the data output to the data bus
for n in self.data_bus_names:
for i in range(2):
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_center_via(("metal2","via2","metal3"),rail_pos)
self.route_single_msb_address()
# connect the banks to the vertical address bus
# connect the banks to the vertical control bus
for n in self.addr_bus_names + self.control_bus_names:
# Skip these from the horizontal bus
if n in ["vdd", "gnd"]: continue
# This will be the bank select, so skip it
if n == self.msb_bank_sel_addr: continue
pin0_pos = self.bank_inst[0].get_pin(n).rc()
pin1_pos = self.bank_inst[1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_center_via(("metal2","via2","metal3"),rail_pos)
# connect the bank select signals to the vertical bus
for i in range(2):
pin = self.bank_inst[i].get_pin("bank_sel")
pin_pos = pin.rc() if i==0 else pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,pin_pos.y)
self.add_path("metal3",[pin_pos,rail_pos])
self.add_center_via(("metal2","via2","metal3"),rail_pos)
self.route_two_bank_supplies()
def route_two_bank_supplies(self):
""" Finish routing the power supplies for a 2-bank SRAM """
# control logic supplies done with control signals
self.route_major_supply_rails()
def route_single_msb_address(self):
""" Route one MSB address bit for 2-bank SRAM """
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1": continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_center_via(("metal1","via1","metal2"),down_pos,rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_center_via(("metal1","via1","metal2"),rail_pos)
gnd_pins = self.msb_address_inst.get_pins("gnd")
# Only add the ground connection to the lowest metal2 rail in the flop array
# FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those
lowest_y = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2": continue
if lowest_y==None or gnd_pin.by()<lowest_y:
lowest_y=gnd_pin.by()
gnd_pos = gnd_pin.ur()
rail_pos = vector(gnd_pos.x,self.horz_control_bus_positions["gnd"].y)
self.add_path("metal2",[gnd_pos,rail_pos])
self.add_center_via(("metal1","via1","metal2"),rail_pos)
# connect the MSB flop to the address input bus
msb_pins = self.msb_address_inst.get_pins("din[0]")
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_center_via(("metal2","via2","metal3"),rail_pos)
# Connect the output bar to select 0
msb_out_pin = self.msb_address_inst.get_pin("dout_bar[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(self.m2_pitch,0)
out_extend_up_pos = out_extend_right_pos + vector(0,self.m2_width)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[0]"].x,out_extend_up_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_up_pos,rail_pos])
self.add_center_via(("metal2","via2","metal3"),rail_pos)
# Connect the output to select 1
msb_out_pin = self.msb_address_inst.get_pin("dout[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(self.m2_pitch,0)
out_extend_down_pos = out_extend_right_pos - vector(0,2*self.m1_pitch)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[1]"].x,out_extend_down_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_down_pos,rail_pos])
self.add_center_via(("metal2","via2","metal3"),rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
def route_major_supply_rails(self):
""" Create rails at bottom. Connect veritcal rails to top and bottom. """
self.add_layout_pin(text="gnd",
layer="metal3",
offset=vector(0,0),
height=self.power_rail_width,
width=self.width)
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vector(0,self.power_rail_pitch),
height=self.power_rail_width,
width=self.width)
# route bank vertical rails to top and bottom
for i in range(2):
vdd_pins = self.bank_inst[i].get_pins("vdd")
for vdd_pin in vdd_pins:
vdd_pos = vdd_pin.ul()
# Route to top
self.add_rect(layer="metal1",
offset=vdd_pos,
height=self.horz_control_bus_positions["vdd"].y-vdd_pos.y+0.5*self.m1_width,
width=vdd_pin.width())
# Route to bottom
self.add_rect(layer="metal1",
offset=vector(vdd_pos.x,self.power_rail_pitch),
height=self.bank_inst[0].by(),
width=vdd_pin.width())
gnd_pins = self.bank_inst[i].get_pins("gnd")
for gnd_pin in gnd_pins:
# Route to top
gnd_pos = gnd_pin.ul()
self.add_rect(layer="metal2",
offset=gnd_pos,
height=self.horz_control_bus_positions["gnd"].y-gnd_pos.y+0.5*self.m1_width,
width=gnd_pin.width())
# Add two vias for right now, should be right size power via
right_rail_pos = vector(gnd_pin.ur().x,self.horz_control_bus_positions["gnd"].y)
self.add_via(layers=("metal1","via1","metal2"),
offset=right_rail_pos - vector(0,0.5*self.m1_width),
rotate=90,
size=[1,3])
# Route to bottom
self.add_rect(layer="metal2",
offset=vector(gnd_pos.x,0),
height=self.bank_inst[0].by(),
width=gnd_pin.width())
# Add two vias for right now, should be right size power via
right_rail_pos = vector(gnd_pin.lr().x,0)
self.add_via(layers=("metal2","via2","metal3"),
offset=right_rail_pos,
rotate=90,
size=[2,3])
# connect the vertical rails along bottom of SRAM
pass
def create_multi_bank_modules(self):
""" Add the multibank address flops and bank decoder """
""" Create the multibank address flops and bank decoder """
self.msf_msb_address = self.mod_ms_flop_array(name="msf_msb_address",
columns=self.num_banks/2,
word_size=self.num_banks/2)
self.add_mod(self.msf_msb_address)
self.msb_decoder = self.bank.decoder.pre2_4
self.add_mod(self.msb_decoder)
self.msb_address = self.mod_ms_flop_array(name="msb_address",
columns=1,
word_size=self.num_banks/2)
self.add_mod(self.msb_address)
if self.num_banks>2:
self.msb_decoder = self.bank.decoder.pre2_4
self.add_mod(self.msb_decoder)
def create_modules(self):
""" Create all the modules that will be used """
# Create the control logic module
self.control = self.mod_control_logic(num_rows=self.num_rows)
self.add_mod(self.control)
self.control_logic = self.mod_control_logic(num_rows=self.num_rows)
self.add_mod(self.control_logic)
# Create the bank module (up to four are instantiated)
self.bank = bank(word_size=self.word_size,
@ -175,16 +521,17 @@ class sram(design.design):
# Conditionally create the
if(self.num_banks > 1):
self.create_multibank_modules()
self.create_multi_bank_modules()
self.bank_count = 0
self.power_rail_width = self.bank.vdd_rail_width
self.sram_power_rail_gap = 4*self.bank.vdd_rail_width
# Leave some extra space for the pitch
self.power_rail_pitch = self.bank.vdd_rail_width + 2*drc["metal3_to_metal3"]
def add_bank(self, position, x_flip, y_flip):
def add_bank(self, bank_num, position, x_flip, y_flip):
""" Place a bank at the given position with orientations """
# x_flip == 1 --> no flip in x_axis
@ -208,7 +555,7 @@ class sram(design.design):
else:
bank_mirror = "R0"
bank_inst=self.add_inst(name="bank{0}".format(self.bank_count),
bank_inst=self.add_inst(name="bank{0}".format(bank_num),
mod=self.bank,
offset=position,
mirror=bank_mirror,
@ -220,66 +567,71 @@ class sram(design.design):
for i in range(self.bank_addr_size):
temp.append("ADDR[{0}]".format(i))
if(self.num_banks > 1):
temp.append("bank_sel[{0}]".format(self.bank_count))
temp.append("bank_sel[{0}]".format(bank_num))
temp.extend(["s_en", "w_en", "tri_en_bar", "tri_en",
"clk_bar","clk_buf" , "vdd", "gnd"])
self.connect_inst(temp)
self.bank_count = self.bank_count + 1
return bank_inst
# FIXME: This should be in geometry.py or it's own class since it is
# reusable
def create_bus(self, layer, offset, bits, height, rotate):
""" Create a bus and place it according to rotate and
return an array of line positions """
def create_bus(self, layer, pitch, offset, names, length, vertical=False, make_pins=False):
""" Create a horizontal or vertical bus. It can be either just rectangles, or actual
layout pins. It returns an map of line center line positions indexed by name. """
minwidth = "minwidth_{0}".format(layer)
m2m = "{0}_to_{0}".format(layer)
line_width = drc[minwidth]
line_gap = 2*drc[m2m]
line_positions = []
bus_width = bits*(line_width + line_gap)
if(rotate == 0):
for i in range(bits):
line_offset = offset + vector(i*(line_width + line_gap),0)
self.add_rect(layer=layer,
offset=line_offset,
width=line_width,
height=height)
line_positions.append(line_offset)
elif(rotate == 270):
for i in range(bits):
line_offset = offset - vector(0, (i+1)*line_width + i*line_gap)
self.add_rect(layer=layer,
offset=line_offset,
width=height,
height=line_width)
line_positions.append(line_offset)
# half minwidth so we can return the center line offsets
half_minwidth = 0.5*drc["minwidth_{}".format(layer)]
line_positions = {}
if vertical:
for i in range(len(names)):
line_offset = offset + vector(i*pitch,0)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
offset=line_offset,
height=length)
else:
self.add_rect(layer=layer,
offset=line_offset,
height=length)
line_positions[names[i]]=line_offset+vector(half_minwidth,0)
else:
debug.error("Unimplemented rotation for create_bus")
for i in range(len(names)):
line_offset = offset + vector(0,i*pitch + half_minwidth)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
offset=line_offset,
width=length)
else:
self.add_rect(layer=layer,
offset=line_offset,
width=length)
line_positions[names[i]]=line_offset+vector(0,half_minwidth)
return line_positions
def calculate_bus_width(self, layer, bits):
""" Calculate the bus width """
minwidth = "minwidth_{0}".format(layer)
m2m = "{0}_to_{0}".format(layer)
line_width = drc[minwidth]
line_gap = 2*drc[m2m]
return bits*(line_width + line_gap) - line_gap
def add_control_logic(self, position, rotate):
""" Add and place control logic """
self.control_logic_inst=self.add_inst(name="control",
mod=self.control,
mod=self.control_logic,
offset=position,
rotate=rotate)
temp = ["CSb", "WEb", "OEb", "clk", "s_en", "w_en", "tri_en",
"tri_en_bar", "clk_bar", "clk_buf", "vdd", "gnd"]
self.connect_inst(temp)
self.connect_inst(self.control_logic_inputs + self.control_logic_outputs + ["vdd", "gnd"])
def add_lvs_correspondence_points(self):
for n in self.control_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="metal2",
offset=self.vert_control_bus_positions[n])
def add_single_bank_modules(self):
"""
@ -288,7 +640,7 @@ class sram(design.design):
"""
# No orientation or offset
self.bank_inst = self.add_bank([0, 0], 1, 1)
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
# Control logic is placed to the left of the blank even with the
# decoder bottom. A small gap is in the x-dimension.
@ -298,7 +650,7 @@ class sram(design.design):
self.add_control_logic(position=pos,
rotate=90)
self.width = self.bank.width + self.control.height + control_gap
self.width = self.bank.width + self.control_logic.height + control_gap
self.height = self.bank.height
def add_single_bank_pins(self):
@ -318,162 +670,30 @@ class sram(design.design):
self.copy_layout_pin(self.bank_inst, "vdd")
self.copy_layout_pin(self.bank_inst, "gnd")
def add_multi_bank_modules(self):
""" This creates either a 2 or 4 bank SRAM with control logic
and bank selection logic."""
self.bank_h = self.bank.height
self.bank_w = self.bank.width
self.num_vertical_line = self.bank_addr_size + self.control_size \
+ self.num_banks + self.num_banks/2
self.num_horizontal_line = self.word_size
self.vertical_bus_width = self.calculate_bus_width("metal2", self.num_vertical_line)
self.horizontal_bus_width = self.calculate_bus_width("metal3", self.num_horizontal_line)
self.vertical_bus_height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \
+ self.horizontal_bus_width
self.horizontal_bus_height = (2 * (self.bank_w + self.bank_to_bus_distance)
+ self.vertical_bus_width)
self.vertical_bus_offset = vector(self.bank_w + self.bank_to_bus_distance,
self.sram_power_rail_gap)
self.horizontal_bus_offset = vector(0,
self.bank_h + self.bank_to_bus_distance
+ self.sram_power_rail_gap
+ self.horizontal_bus_width)
# Vertical bus
self.vertical_line_positions = self.create_bus(layer="metal2",
offset=self.vertical_bus_offset,
bits=self.num_vertical_line,
height=self.vertical_bus_height,
rotate=0)
# Horizontal bus
self.horizontal_line_positions = self.create_bus(layer="metal3",
offset=self.horizontal_bus_offset,
bits=self.num_horizontal_line,
height=self.horizontal_bus_height,
rotate=270)
for i in range(0, self.word_size):
self.add_label(text="DATA[{0}]".format(i),
layer="metal3",
offset=self.horizontal_line_positions[i])
self.width = 2*(self.bank_w + self.bank_to_bus_distance) + self.vertical_bus_width
self.height = (self.num_banks/2)*(self.bank_h + self.bank_to_bus_distance) \
+ self.horizontal_bus_width + self.sram_power_rail_gap
# Add Control logic for Bank = 2 and Bank =4
control_bus_width = self.calculate_bus_width("metal1",
self.control_size + 2)
control_bus_height = (self.vertical_line_positions[self.control_size - 1].x
+ drc["minwidth_metal2"])
control_bus_offset = vector(0, self.height + control_bus_width
+ 4*drc["minwidth_metal3"])
self.control_bus_line_positions = self.create_bus(layer="metal1",
offset=control_bus_offset,
bits=self.control_size + 2,
height=control_bus_height,
rotate=270)
if (self.num_banks == 2):
self.control_position = vector(0, control_bus_offset.y
+ self.ms_flop_chars["width"])
self.add_control_logic(self.control_position, 0)
self.CSb_position = self.control_position + self.control.CSb_position
self.OEb_position = self.control_position + self.control.OEb_position
self.WEb_position = self.control_position + self.control.WEb_position
self.clk_position = self.control_position + self.control.clk_position
# Max point
self.max_point = self.control_position.y + self.ms_flop_chars["width"]
# MSB address
x_off = (self.bank_w + self.vertical_bus_width
+ 2 * self.bank_to_bus_distance
+ self.power_rail_width
+ 4 * drc["minwidth_metal3"])
y_off = self.height + 2 * self.ms_flop_chars["width"] + 4 * drc["minwidth_metal3"]
self.msf_msb_address_position = vector(x_off, y_off)
self.add_inst(name="msf_msb_address",
mod=self.msf_msb_address,
offset=self.msf_msb_address_position,
mirror="RO",
rotate=270)
temp = []
for i in range(self.num_banks/2):
temp.append("ADDR[{0}]".format(self.bank.addr_size + i))
if(self.num_banks == 4):
for i in range(self.num_banks/2):
temp.append("msb{0}".format(i))
temp.append("msb{0}_bar".format(i))
else:
temp.extend(["bank_sel[1]", "bank_sel[0]"])
temp.extend(["clk_buf", "vdd", "gnd"])
self.connect_inst(temp)
self.add_top_banks()
if self.num_banks == 4:
self.add_bottom_banks()
# Extension of Vertical Rail
self.create_bus(layer="metal2",
offset=[self.vertical_bus_offset.x,
self.height],
bits=self.num_vertical_line,
height=self.max_point - self.height,
rotate=0)
# Add ADDRESS labels to vertical line
for i in range(self.addr_size - int(math.log(self.num_banks, 2))):
index = self.control_size + int(math.log(self.num_banks, 2)) + i
self.add_label(text="ADDR[{}]".format(i),
layer="metal2",
offset=[self.vertical_line_positions[index].x,
self.max_point])
for i in range(int(math.log(self.num_banks, 2))):
self.add_label(text="ADDR[{}]".format(self.addr_size - i - 1),
layer="metal2",
offset=[self.vertical_line_positions[self.control_size + i].x,
self.max_point])
def add_top_banks(self):
# Placement of bank 0
self.bank_position_0 = vector(self.bank_w,
self.bank_h + self.sram_power_rail_gap)
self.add_bank(self.bank_position_0, -1, -1)
self.bank_position_0 = vector(self.bank.width,
self.bank.height + 2*self.power_rail_pitch)
self.bank_inst=[self.add_bank(0,self.bank_position_0, -1, -1)]
# Placement of bank 1
x_off = self.bank_w + self.vertical_bus_width + 2*self.bank_to_bus_distance
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
self.bank_position_1 = vector(x_off, self.bank_position_0.y)
self.add_bank(self.bank_position_1, -1, 1)
self.bank_inst.append(self.add_bank(1,self.bank_position_1, -1, 1))
def add_bottom_banks(self):
# Placement of bank 2
y_off = (self.bank_h + self.horizontal_bus_width
+2 * self.bank_to_bus_distance
+ self.sram_power_rail_gap)
y_off = self.bank.height + self.horizontal_bus_width + 2*self.bank_to_bus_distance + self.power_rail_pitch
bank_position_2 = vector(self.bank_position_0.x, y_off)
self.add_bank(bank_position_2, 1, -1)
self.bank2=self.add_bank(bank_position_2, 1, -1)
# Placement of bank 3
bank_position_3 = vector(self.bank_position_1.x, bank_position_2.y)
self.add_bank(bank_position_3, 1, 1)
self.bank3=self.add_bank(bank_position_3, 1, 1)
self.msb_decoder_position = vector(bank_position_3.x + self.power_rail_width
+ 4 * drc["minwidth_metal3"]
+ self.msb_decoder.width,
self.msf_msb_address_position.y
+ 4 * drc["minwidth_metal3"])
self.msb_decoder_position = vector(bank_position_3.x + self.power_rail_width + 4*drc["minwidth_metal3"] + self.msb_decoder.width,
self.msf_msb_address_position.y + 4*drc["minwidth_metal3"])
self.add_inst(name="msb_decoder",
mod=self.msb_decoder,
@ -485,28 +705,23 @@ class sram(design.design):
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
self.control_position = vector(0, self.msb_decoder_position.y
+ self.msb_decoder.height)
self.control_position = vector(0, self.msb_decoder_position.y + self.msb_decoder.height)
self.add_control_logic(self.control_position, 0)
self.CSb_position = self.control_position + self.control.CSb_position
self.OEb_position = self.control_position + self.control.OEb_position
self.WEb_position = self.control_position + self.control.WEb_position
self.clk_position = self.control_position + self.control.clk_position
# Max point
self.max_point = self.msb_decoder_position.y + self.msb_decoder.height
def route_top_banks(self):
def route_2or4_banks(self):
""" Routing of top two banks """
addr_start_index = len(self.sram_property) + (self.num_banks / 2)
return
addr_start_index = len(self.sram_property) + self.num_banks/2
bank_select_index = addr_start_index + self.bank.addr_size
# control, data , address and bank_select connection
for i in range(self.num_banks / 2):
left_bank_index = 2 * i
right_bank_index = 2 * i + 1
left_bank_index = 2*i
right_bank_index = 2*i + 1
for attr_index in range(len(self.sram_property)):
bank_attr = self.sram_property[attr_index]
@ -602,7 +817,7 @@ class sram(design.design):
out_pos = vector(dest_pin.cx(), in_pos.y)
self.add_wire(("metal3","via2","metal2"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
self.add_via(layers=("metal2","via2","metal3"),
offset=src_pin.lr(),
offset=src_pin.lr() - self.m2m3_offset_fix,
rotate=90)
def connect_rail_from_left_m2m1(self, src_pin, dest_pin):
@ -613,16 +828,9 @@ class sram(design.design):
def route_single_bank(self):
""" Route a single bank SRAM """
# left pin is on the control logic, right pin is on the bank
connections = [("clk_buf", "clk"),
("tri_en_bar", "tri_en_bar"),
("tri_en", "tri_en"),
("clk_bar", "clk_bar"),
("w_en", "w_en"),
("s_en", "s_en")]
for (src,dest) in connections:
src_pin = self.control_logic_inst.get_pin(src)
dest_pin = self.bank_inst.get_pin(dest)
for n in self.control_logic_outputs:
src_pin = self.control_logic_inst.get_pin(n)
dest_pin = self.bank_inst.get_pin(n)
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
src_pins = self.control_logic_inst.get_pins("vdd")

View File

@ -29,7 +29,7 @@ class nor_2_test(unittest.TestCase):
import tech
debug.info(2, "Checking 2-input nor gate")
tx = nor_2.nor_2(nmos_width=2 * tech.drc["minwidth_tx"])
tx = nor_2.nor_2()
self.local_check(tx)
OPTS.check_lvsdrc = True

View File

@ -25,20 +25,20 @@ class multi_bank_test(unittest.TestCase):
import bank
debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="test_bank1")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=2, name="bank1")
self.local_check(a)
debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="test_bank2")
self.local_check(a)
#a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=2, name="bank2")
#self.local_check(a)
debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="test_bank3")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=2, name="bank3")
self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail
# debug.info(1, "Eight way column mux")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="test_bank4")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4")
# self.local_check(a)
OPTS.check_lvsdrc = True

View File

@ -26,20 +26,20 @@ class single_bank_test(unittest.TestCase):
import bank
debug.info(1, "No column mux")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="test_bank1")
a = bank.bank(word_size=4, num_words=16, words_per_row=1, num_banks=1, name="bank1")
self.local_check(a)
debug.info(1, "Two way column mux")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="test_bank2")
a = bank.bank(word_size=4, num_words=32, words_per_row=2, num_banks=1, name="bank2")
self.local_check(a)
debug.info(1, "Four way column mux")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="test_bank3")
a = bank.bank(word_size=4, num_words=64, words_per_row=4, num_banks=1, name="bank3")
self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail
# debug.info(1, "Eight way column mux")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="test_bank4")
# a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4")
# self.local_check(a)
OPTS.check_lvsdrc = True

View File

@ -26,19 +26,19 @@ class sram_1bank_test(unittest.TestCase):
import sram
debug.info(1, "Single bank, no column mux with control logic")
a = sram.sram(word_size=4, num_words=16, num_banks=1, name="test_sram1")
a = sram.sram(word_size=4, num_words=16, num_banks=1, name="sram1")
self.local_check(a)
debug.info(1, "Single bank two way column mux with control logic")
a = sram.sram(word_size=4, num_words=32, num_banks=1, name="test_sram2")
a = sram.sram(word_size=4, num_words=32, num_banks=1, name="sram2")
self.local_check(a)
debug.info(1, "Single bank, four way column mux with control logic")
a = sram.sram(word_size=4, num_words=64, num_banks=1, name="test_sram3")
a = sram.sram(word_size=4, num_words=64, num_banks=1, name="sram3")
self.local_check(a)
# debug.info(1, "Single bank, eight way column mux with control logic")
# a = sram.sram(word_size=2, num_words=128, num_banks=1, name="test_sram4")
# a = sram.sram(word_size=2, num_words=128, num_banks=1, name="sram4")
# self.local_check(a)
OPTS.check_lvsdrc = True

View File

@ -26,19 +26,19 @@ class sram_2bank_test(unittest.TestCase):
import sram
debug.info(1, "Two bank, no column mux with control logic")
a = sram.sram(word_size=4, num_words=32, num_banks=2, name="test_sram1")
a = sram.sram(word_size=16, num_words=32, num_banks=2, name="sram1")
self.local_check(a)
debug.info(1, "Two bank two way column mux with control logic")
a = sram.sram(word_size=4, num_words=64, num_banks=2, name="test_sram2")
a = sram.sram(word_size=16, num_words=64, num_banks=2, name="sram2")
self.local_check(a)
debug.info(1, "Two bank, four way column mux with control logic")
a = sram.sram(word_size=4, num_words=128, num_banks=2, name="test_sram3")
a = sram.sram(word_size=16, num_words=128, num_banks=2, name="sram3")
self.local_check(a)
# debug.info(1, "Two bank, eight way column mux with control logic")
# a = sram.sram(word_size=2, num_words=256 num_banks=2, name="test_sram4")
# a = sram.sram(word_size=2, num_words=256 num_banks=2, name="sram4")
# self.local_check(a)
OPTS.check_lvsdrc = True

View File

@ -26,19 +26,19 @@ class sram_4bank_test(unittest.TestCase):
import sram
debug.info(1, "Four bank, no column mux with control logic")
a = sram.sram(word_size=4, num_words=32, num_banks=4, name="test_sram1")
a = sram.sram(word_size=4, num_words=32, num_banks=4, name="sram1")
self.local_check(a)
debug.info(1, "Four bank two way column mux with control logic")
a = sram.sram(word_size=4, num_words=64, num_banks=4, name="test_sram2")
a = sram.sram(word_size=4, num_words=64, num_banks=4, name="sram2")
self.local_check(a)
debug.info(1, "Four bank, four way column mux with control logic")
a = sram.sram(word_size=4, num_words=128, num_banks=4, name="test_sram3")
a = sram.sram(word_size=4, num_words=128, num_banks=4, name="sram3")
self.local_check(a)
# debug.info(1, "Four bank, eight way column mux with control logic")
# a = sram.sram(word_size=2, num_words=256, num_banks=4, name="test_sram4")
# a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4")
# self.local_check(a)
OPTS.check_lvsdrc = True

View File

@ -32,7 +32,7 @@ class timing_sram_test(unittest.TestCase):
s = sram.sram(word_size=OPTS.config.word_size,
num_words=OPTS.config.num_words,
num_banks=OPTS.config.num_banks,
name="test_sram1")
name="sram1")
OPTS.check_lvsdrc = True
@ -52,25 +52,25 @@ class timing_sram_test(unittest.TestCase):
data = d.analyze(probe_address, probe_data,slews,loads)
if OPTS.tech_name == "freepdk45":
golden_data = {'read1_power': 0.017787999999999998,
'read0_power': 0.017827,
'write0_power': 0.016626,
'delay1': [0.02616],
'delay0': [0.10966999999999999],
'min_period': 0.264,
'write1_power': 0.015919000000000003,
'slew0': [0.027029],
'slew1': [0.021002999999999997]}
golden_data = {'read1_power': 0.025791799999999997,
'read0_power': 0.0260092,
'write0_power': 0.0241064,
'delay1': [0.0475006],
'delay0': [0.1380874],
'min_period': 0.322,
'write1_power': 0.024207199999999998,
'slew0': [0.026617000000000002],
'slew1': [0.0193804]}
elif OPTS.tech_name == "scn3me_subm":
golden_data = {'read1_power': 4.5206,
'read0_power': 4.5492,
'write0_power': 3.8564,
'delay1': [0.5985562],
'delay0': [1.3725000000000003],
'min_period': 4.531,
'write1_power': 3.7291,
'slew0': [1.3013000000000001],
'slew1': [1.0045]}
golden_data = {'read1_power': 3.1765,
'read0_power': 3.1929,
'write0_power': 2.874,
'delay1': [0.8900045999999999],
'delay0': [1.9975000000000003],
'min_period': 5.781,
'write1_power': 2.6611,
'slew0': [1.2993000000000001],
'slew1': [0.9903856]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -30,7 +30,7 @@ class timing_sram_test(unittest.TestCase):
s = sram.sram(word_size=OPTS.config.word_size,
num_words=OPTS.config.num_words,
num_banks=OPTS.config.num_banks,
name="test_sram1")
name="sram1")
import delay
@ -48,25 +48,25 @@ class timing_sram_test(unittest.TestCase):
data = d.analyze(probe_address, probe_data,slews,loads)
if OPTS.tech_name == "freepdk45":
golden_data = {'read1_power': 0.022260799999999997,
'read0_power': 0.02274298,
'write0_power': 0.02000899,
'delay1': [0.026754629999999998],
'delay0': [0.1126814],
'min_period': 0.273,
'write1_power': 0.01934197,
'slew0': [0.02760651],
'slew1': [0.023076919999999997]}
golden_data = {'read1_power': 0.02527215,
'read0_power': 0.02573022,
'write0_power': 0.02237065,
'delay1': [0.04867785],
'delay0': [0.1423512],
'min_period': 0.332,
'write1_power': 0.02152122,
'slew0': [0.0273352],
'slew1': [0.021216870000000002]}
elif OPTS.tech_name == "scn3me_subm":
golden_data = {'read1_power': 5.549996,
'read0_power': 4.781156,
'write0_power': 3.931431,
'delay1': [0.6227914],
'delay0': [1.414657],
'min_period': 4.688,
'write1_power': 3.409661,
'slew0': [1.345377],
'slew1': [1.05667]}
golden_data = {'read1_power': 3.244839,
'read0_power': 3.088234,
'write0_power': 2.6857420000000003,
'delay1': [0.9200643],
'delay0': [2.0509399999999998],
'min_period': 6.563,
'write1_power': 2.378355,
'slew0': [1.342019],
'slew1': [1.040885]}
else:
self.assertTrue(False) # other techs fail

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 799.659625;
area : 692.2795;
bus(DATA){
bus_type : DATA;
@ -92,10 +92,10 @@ cell (sram_2_16_1_freepdk45){
internal_power(){
when : "OEb & !clk";
rise_power(scalar){
values("0.020625");
values("0.0287643");
}
fall_power(scalar){
values("0.021124");
values("0.0284106");
}
}
timing(){
@ -129,10 +129,10 @@ cell (sram_2_16_1_freepdk45){
internal_power(){
when : "!OEb & !clk";
rise_power(scalar){
values("0.023099");
values("0.0320149");
}
fall_power(scalar){
values("0.023401");
values("0.0322925");
}
}
timing(){
@ -140,24 +140,24 @@ cell (sram_2_16_1_freepdk45){
related_pin : "clk";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.025, 0.026, 0.032",\
"0.026, 0.026, 0.033",\
"0.03, 0.031, 0.037");
values("0.046, 0.046, 0.053",\
"0.046, 0.047, 0.054",\
"0.051, 0.052, 0.059");
}
cell_fall(CELL_TABLE) {
values("0.11, 0.11, 0.118",\
"0.11, 0.111, 0.119",\
"0.114, 0.114, 0.122");
values("0.142, 0.143, 0.152",\
"0.143, 0.144, 0.152",\
"0.148, 0.149, 0.158");
}
rise_transition(CELL_TABLE) {
values("0.016, 0.017, 0.028",\
"0.016, 0.017, 0.028",\
"0.016, 0.018, 0.028");
values("0.014, 0.015, 0.027",\
"0.014, 0.015, 0.027",\
"0.014, 0.015, 0.027");
}
fall_transition(CELL_TABLE) {
values("0.02, 0.022, 0.037",\
"0.02, 0.022, 0.037",\
"0.021, 0.023, 0.037");
values("0.019, 0.02, 0.036",\
"0.019, 0.02, 0.036",\
"0.019, 0.02, 0.036");
}
}
}
@ -308,20 +308,20 @@ cell (sram_2_16_1_freepdk45){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(scalar) {
values("0.166");
values("0.205");
}
fall_constraint(scalar) {
values("0.166");
values("0.205");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("0.332");
values("0.41");
}
fall_constraint(scalar) {
values("0.332");
values("0.41");
}
}
}

View File

@ -74,7 +74,7 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 799.659625;
area : 692.2795;
bus(DATA){
bus_type : DATA;
@ -140,14 +140,14 @@ cell (sram_2_16_1_freepdk45){
related_pin : "clk";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.12, 0.121, 0.131",\
"0.12, 0.121, 0.131",\
"0.12, 0.121, 0.131");
values("0.122, 0.123, 0.132",\
"0.122, 0.123, 0.132",\
"0.122, 0.123, 0.132");
}
cell_fall(CELL_TABLE) {
values("0.12, 0.121, 0.131",\
"0.12, 0.121, 0.131",\
"0.12, 0.121, 0.131");
values("0.122, 0.123, 0.132",\
"0.122, 0.123, 0.132",\
"0.122, 0.123, 0.132");
}
rise_transition(CELL_TABLE) {
values("0.006, 0.007, 0.018",\

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 102102.39;
area : 89304.3;
bus(DATA){
bus_type : DATA;
@ -92,10 +92,10 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){
when : "OEb & !clk";
rise_power(scalar){
values("2.3875");
values("3.1588");
}
fall_power(scalar){
values("2.5832");
values("3.4922");
}
}
timing(){
@ -129,10 +129,10 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){
when : "!OEb & !clk";
rise_power(scalar){
values("2.9868");
values("3.9389");
}
fall_power(scalar){
values("3.0093");
values("3.9642");
}
}
timing(){
@ -140,24 +140,24 @@ cell (sram_2_16_1_scn3me_subm){
related_pin : "clk";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.261, 0.343, 1.013",\
"0.264, 0.345, 1.019",\
"0.311, 0.39, 1.062");
values("0.542, 0.626, 1.298",\
"0.545, 0.628, 1.304",\
"0.594, 0.674, 1.35");
}
cell_fall(CELL_TABLE) {
values("0.837, 0.945, 1.919",\
"0.841, 0.95, 1.925",\
"0.878, 0.986, 1.96");
values("1.532, 1.634, 2.6",\
"1.536, 1.639, 2.607",\
"1.587, 1.69, 2.657");
}
rise_transition(CELL_TABLE) {
values("0.21, 0.354, 1.887",\
"0.214, 0.356, 1.888",\
"0.313, 0.361, 1.889");
values("0.191, 0.337, 1.884",\
"0.193, 0.338, 1.885",\
"0.195, 0.341, 1.884");
}
fall_transition(CELL_TABLE) {
values("0.236, 0.445, 2.463",\
"0.236, 0.445, 2.462",\
"0.235, 0.445, 2.454");
values("0.255, 0.448, 2.467",\
"0.256, 0.447, 2.468",\
"0.256, 0.447, 2.454");
}
}
}
@ -308,20 +308,20 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(scalar) {
values("3.75");
values("4.5315");
}
fall_constraint(scalar) {
values("3.75");
values("4.5315");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("7.5");
values("9.063");
}
fall_constraint(scalar) {
values("7.5");
values("9.063");
}
}
}

View File

@ -74,7 +74,7 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 102102.39;
area : 89304.3;
bus(DATA){
bus_type : DATA;
@ -140,14 +140,14 @@ cell (sram_2_16_1_scn3me_subm){
related_pin : "clk";
timing_type : falling_edge;
cell_rise(CELL_TABLE) {
values("0.57, 0.617, 1.058",\
"0.57, 0.617, 1.058",\
"0.57, 0.617, 1.058");
values("0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041");
}
cell_fall(CELL_TABLE) {
values("0.57, 0.617, 1.058",\
"0.57, 0.617, 1.058",\
"0.57, 0.617, 1.058");
values("0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041",\
"0.553, 0.6, 1.041");
}
rise_transition(CELL_TABLE) {
values("0.024, 0.081, 0.61",\