mirror of https://github.com/VLSIDA/OpenRAM.git
Two bank SRAMs working in both technologies.
This commit is contained in:
parent
d29dd03373
commit
e06e1691c8
238
compiler/bank.py
238
compiler/bank.py
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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. """
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
119
compiler/ptx.py
119
compiler/ptx.py
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
660
compiler/sram.py
660
compiler/sram.py
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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",\
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue