Merge multiport

This commit is contained in:
Matt Guthaus
2018-10-08 11:45:50 -07:00
182 changed files with 2437 additions and 4307755 deletions
+184 -153
View File
@@ -29,10 +29,6 @@ class bank(design.design):
design.design.__init__(self, name)
debug.info(2, "create sram of size {0} with {1} words".format(self.word_size,self.num_words))
self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports
self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports
self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals to create
@@ -53,6 +49,7 @@ class bank(design.design):
self.add_modules()
self.create_modules()
def create_layout(self):
self.place_modules()
self.setup_routing_constraints()
@@ -65,19 +62,9 @@ class bank(design.design):
self.bank_center=self.offset_all_coordinates().scale(-1,-1)
self.DRC_LVS()
def add_pins(self):
self.read_index = []
port_number = 0
for port in range(OPTS.num_rw_ports):
self.read_index.append("{}".format(port_number))
port_number += 1
for port in range(OPTS.num_w_ports):
port_number += 1
for port in range(OPTS.num_r_ports):
self.read_index.append("{}".format(port_number))
port_number += 1
""" Adding pins for Bank module"""
for port in range(self.total_read):
for bit in range(self.word_size):
@@ -98,11 +85,13 @@ class bank(design.design):
self.add_pin("s_en{0}".format(self.read_index[port]), "INPUT")
for port in range(self.total_write):
self.add_pin("w_en{0}".format(port), "INPUT")
for pin in ["clk_buf_bar","clk_buf"]:
self.add_pin(pin,"INPUT")
for port in range(self.total_ports):
self.add_pin("clk_buf_bar{0}".format(port),"INPUT")
self.add_pin("clk_buf{0}".format(port),"INPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
def route_layout(self):
""" Create routing amoung the modules """
self.route_central_bus()
@@ -119,6 +108,7 @@ class bank(design.design):
self.route_bank_select()
self.route_vdd_gnd()
def create_modules(self):
""" Add modules. The order should not matter! """
@@ -156,10 +146,10 @@ class bank(design.design):
self.place_row_decoder()
self.place_wordline_driver()
self.place_column_decoder()
self.place_bank_select()
def compute_sizes(self):
""" Computes the required sizes to create the bank """
@@ -181,13 +171,25 @@ class bank(design.design):
# Number of control lines in the bus
self.num_control_lines = 4
# The order of the control signals on the control bus:
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"]
self.input_control_signals = []
port_num = 0
for port in range(OPTS.num_rw_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_w_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num)])
port_num += 1
for port in range(OPTS.num_r_ports):
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "s_en{}".format(port_num)])
port_num += 1
# These will be outputs of the gaters if this is multibank, if not, normal signals.
if self.num_banks > 1:
self.control_signals = ["gated_"+str for str in self.input_control_signals]
else:
self.control_signals = self.input_control_signals
self.control_signals = []
for port in range(self.total_ports):
if self.num_banks > 1:
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
else:
self.control_signals.append(self.input_control_signals[port])
# The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size
@@ -247,8 +249,8 @@ class bank(design.design):
for port in range(self.total_ports):
self.column_mux_array.append(self.mod_column_mux_array(columns=self.num_cols,
word_size=self.word_size,
bitcell_bl=self.read_bl_list[port],
bitcell_br=self.read_br_list[port]))
bitcell_bl=self.total_bl_list[port],
bitcell_br=self.total_br_list[port]))
self.add_mod(self.column_mux_array[port])
@@ -292,6 +294,7 @@ class bank(design.design):
temp.append("gnd")
self.connect_inst(temp)
def place_bitcell_array(self):
""" Placing Bitcell Array """
self.bitcell_array_inst.place(vector(0,0))
@@ -308,9 +311,10 @@ class bank(design.design):
for i in range(self.num_cols):
temp.append(self.read_bl_list[port]+"[{0}]".format(i))
temp.append(self.read_br_list[port]+"[{0}]".format(i))
temp.extend([self.prefix+"clk_buf_bar", "vdd"])
temp.extend([self.prefix+"clk_buf_bar{0}".format(self.read_index[port]), "vdd"])
self.connect_inst(temp)
def place_precharge_array(self):
""" Placing Precharge """
@@ -320,6 +324,7 @@ class bank(design.design):
# The enclosure is for the well and the spacing is to the bitcell wells
y_offset = self.bitcell_array.height + self.m2_gap
self.precharge_array_inst[port].place(vector(0,y_offset))
def create_column_mux_array(self):
""" Creating Column Mux when words_per_row > 1 . """
@@ -343,6 +348,7 @@ class bank(design.design):
temp.append("gnd")
self.connect_inst(temp)
def place_column_mux_array(self):
""" Placing Column Mux when words_per_row > 1 . """
if self.col_addr_size > 0:
@@ -354,6 +360,7 @@ class bank(design.design):
for port in range(self.total_ports):
y_offset = self.column_mux_height
self.col_mux_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
def create_sense_amp_array(self):
""" Creating Sense amp """
@@ -373,9 +380,10 @@ class bank(design.design):
temp.append(self.read_bl_list[port]+"_out[{0}]".format(bit))
temp.append(self.read_br_list[port]+"_out[{0}]".format(bit))
temp.extend([self.prefix+"s_en{0}".format(port), "vdd", "gnd"])
temp.extend([self.prefix+"s_en{}".format(self.read_index[port]), "vdd", "gnd"])
self.connect_inst(temp)
def place_sense_amp_array(self):
""" Placing Sense amp """
@@ -383,6 +391,7 @@ class bank(design.design):
for port in range(self.total_read):
y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap
self.sense_amp_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
def create_write_driver_array(self):
""" Creating Write Driver """
@@ -405,6 +414,7 @@ class bank(design.design):
temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"])
self.connect_inst(temp)
def place_write_driver_array(self):
""" Placing Write Driver """
@@ -413,7 +423,6 @@ class bank(design.design):
y_offset = self.sense_amp_array.height + self.column_mux_height \
+ self.m2_gap + self.write_driver_array.height
self.write_driver_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
def create_row_decoder(self):
@@ -432,6 +441,7 @@ class bank(design.design):
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_row_decoder(self):
""" Place the hierarchical row decoder """
@@ -460,11 +470,12 @@ class bank(design.design):
temp.append("dec_out{0}[{1}]".format(port,row))
for row in range(self.num_rows):
temp.append(self.total_wl_list[port]+"[{0}]".format(row))
temp.append(self.prefix+"clk_buf")
temp.append(self.prefix+"clk_buf{0}".format(port))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_wordline_driver(self):
""" Place the Wordline Driver """
@@ -506,6 +517,7 @@ class bank(design.design):
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_column_decoder(self):
"""
Place a 2:4 or 3:8 column address decoder.
@@ -536,12 +548,13 @@ class bank(design.design):
mod=self.bank_select))
temp = []
temp.extend(self.input_control_signals)
temp.extend(self.input_control_signals[port])
temp.append("bank_sel{}".format(port))
temp.extend(self.control_signals)
temp.extend(self.control_signals[port])
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def place_bank_select(self):
""" Place the bank select logic. """
@@ -552,9 +565,9 @@ class bank(design.design):
for port in range(self.total_ports):
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
if self.col_addr_size > 0:
y_off = min(self.col_decoder_inst[0].by(), self.col_mux_array_inst[0].by())
y_off = min(self.col_decoder_inst[port].by(), self.col_mux_array_inst[port].by())
else:
y_off = self.row_decoder_inst[0].by()
y_off = self.row_decoder_inst[port].by()
y_off -= (self.bank_select.height + drc["well_to_well"])
self.bank_select_pos = vector(x_off,y_off)
self.bank_select_inst[port].place(self.bank_select_pos)
@@ -565,18 +578,30 @@ class bank(design.design):
for inst in self.insts:
self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd")
def route_bank_select(self):
""" Route the bank select logic. """
for port in range(self.total_ports):
for input_name in self.input_control_signals+["bank_sel"]:
self.copy_layout_pin(self.bank_select_inst[port], input_name)
for gated_name in self.control_signals:
if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
elif self.port_id[port] == "w":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"]
else:
bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"]
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
for signal in range(len(copy_control_signals)):
self.copy_layout_pin(self.bank_select_inst[port], bank_sel_signals[signal], copy_control_signals[signal])
for signal in range(len(gated_bank_sel_signals)):
# Connect the inverter output to the central bus
out_pos = self.bank_select_inst[port].get_pin(gated_name).rc()
bus_pos = vector(self.bus_xoffset[gated_name].x, out_pos.y)
out_pos = self.bank_select_inst[port].get_pin(gated_bank_sel_signals[signal]).rc()
name = self.control_signals[port][signal]
bus_pos = vector(self.bus_xoffset[name].x, out_pos.y)
self.add_path("metal3",[out_pos, bus_pos])
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=bus_pos,
@@ -594,7 +619,8 @@ class bank(design.design):
After the modules are instantiated, find the dimensions for the
control bus, power ring, etc.
"""
# FIXME: calculate for multiport
#The minimum point is either the bottom of the address flops,
#the column decoder (if there is one).
write_driver_min_y_offset = self.write_driver_array_inst[0].by() - 3*self.m2_pitch
@@ -628,7 +654,6 @@ class bank(design.design):
self.height = ur.y - ll.y
self.width = ur.x - ll.x
def route_central_bus(self):
""" Create the address, supply, and control signal central bus lines. """
@@ -637,16 +662,16 @@ class bank(design.design):
# and control lines.
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
control_bus_length = self.max_y_offset - self.min_y_offset
self.bus_xoffset = self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=self.control_signals,
length=control_bus_length,
vertical=True,
make_pins=(self.num_banks==1))
for port in range(self.total_ports):
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
control_bus_length = self.max_y_offset - self.min_y_offset
self.bus_xoffset = self.create_bus(layer="metal2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=self.control_signals[port],
length=control_bus_length,
vertical=True,
make_pins=(self.num_banks==1))
def route_precharge_to_bitcell_array(self):
@@ -687,7 +712,8 @@ class bank(design.design):
vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br])
def route_sense_amp_to_col_mux_or_bitcell_array(self):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
@@ -711,61 +737,61 @@ class bank(design.design):
vector(connect_bl.x,yoffset), connect_bl])
self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
vector(connect_br.x,yoffset), connect_br])
def route_sense_amp_out(self):
""" Add pins for the sense amp output """
# FIXME: Update for multiport
for bit in range(self.word_size):
data_pin = self.sense_amp_array_inst[0].get_pin("data[{}]".format(bit))
self.add_layout_pin_rect_center(text="dout0[{}]".format(bit),
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width())
for port in range(self.total_read):
for bit in range(self.word_size):
data_pin = self.sense_amp_array_inst[port].get_pin("data[{}]".format(bit))
self.add_layout_pin_rect_center(text="dout{0}[{1}]".format(self.read_index[port],bit),
layer=data_pin.layer,
offset=data_pin.center(),
height=data_pin.height(),
width=data_pin.width())
def route_row_decoder(self):
""" Routes the row decoder inputs and supplies """
# FIXME: Update for multiport
# Create inputs for the row address lines
for row in range(self.row_addr_size):
addr_idx = row + self.col_addr_size
decoder_name = "addr[{}]".format(row)
addr_name = "addr0[{}]".format(addr_idx)
self.copy_layout_pin(self.row_decoder_inst[0], decoder_name, addr_name)
for port in range(self.total_ports):
for row in range(self.row_addr_size):
addr_idx = row + self.col_addr_size
decoder_name = "addr[{}]".format(row)
addr_name = "addr{0}[{1}]".format(port,addr_idx)
self.copy_layout_pin(self.row_decoder_inst[port], decoder_name, addr_name)
def route_write_driver(self):
""" Connecting write driver """
for row in range(self.word_size):
data_name = "data[{}]".format(row)
din_name = "din0[{}]".format(row)
self.copy_layout_pin(self.write_driver_array_inst[0], data_name, din_name)
for port in range(self.total_ports):
for row in range(self.word_size):
data_name = "data[{}]".format(row)
din_name = "din{0}[{1}]".format(port,row)
self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name)
def route_wordline_driver(self):
""" Connecting Wordline driver output to Bitcell WL connection """
for port in range(self.total_ports):
for row in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pos = self.row_decoder_inst[port].get_pin("decode[{}]".format(row)).rc()
driver_in_pos = self.wordline_driver_inst[port].get_pin("in[{}]".format(row)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
for row in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pos = self.row_decoder_inst[0].get_pin("decode[{}]".format(row)).rc()
driver_in_pos = self.wordline_driver_inst[0].get_pin("in[{}]".format(row)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1)
self.add_path("metal1", [decoder_out_pos, mid1, mid2, driver_in_pos])
# The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.wordline_driver_inst[0].get_pin("wl[{}]".format(row)).rc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[0]+"[{}]".format(row)).lc()
mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0)
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
# The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl[{}]".format(row)).rc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[port]+"[{}]".format(row)).lc()
mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0)
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
def route_column_address_lines(self):
@@ -773,49 +799,45 @@ class bank(design.design):
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.col_decoder_inst[0], "A", "addr0[0]")
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out[{}]".format(i))
for i in range(self.col_addr_size):
decoder_name = "in[{}]".format(i)
addr_name = "addr0[{}]".format(i)
self.copy_layout_pin(self.col_decoder_inst[0], decoder_name, addr_name)
for port in range(self.total_ports):
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.col_decoder_inst[port], "A", "addr{}[0]".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out[{}]".format(i))
# This will do a quick "river route" on two layers.
# When above the top select line it will offset "inward" again to prevent conflicts.
# This could be done on a single layer, but we follow preferred direction rules for later routing.
top_y_offset = self.col_mux_array_inst[0].get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy()
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
mux_name = "sel[{}]".format(i)
mux_addr_pos = self.col_mux_array_inst[0].get_pin(mux_name).lc()
decode_out_pos = self.col_decoder_inst[0].get_pin(decode_name).center()
for i in range(self.col_addr_size):
decoder_name = "in[{}]".format(i)
addr_name = "addr{0}[{1}]".format(port,i)
self.copy_layout_pin(self.col_decoder_inst[port], decoder_name, addr_name)
# To get to the edge of the decoder and one track out
delta_offset = self.col_decoder_inst[0].rx() - decode_out_pos.x + self.m2_pitch
if decode_out_pos.y > top_y_offset:
mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y)
else:
mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y)
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
#self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
# This will do a quick "river route" on two layers.
# When above the top select line it will offset "inward" again to prevent conflicts.
# This could be done on a single layer, but we follow preferred direction rules for later routing.
top_y_offset = self.col_mux_array_inst[port].get_pin("sel[{}]".format(self.num_col_addr_lines-1)).cy()
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
mux_name = "sel[{}]".format(i)
mux_addr_pos = self.col_mux_array_inst[port].get_pin(mux_name).lc()
decode_out_pos = self.col_decoder_inst[port].get_pin(decode_name).center()
# To get to the edge of the decoder and one track out
delta_offset = self.col_decoder_inst[port].rx() - decode_out_pos.x + self.m2_pitch
if decode_out_pos.y > top_y_offset:
mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y)
else:
mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y)
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
#self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
def add_lvs_correspondence_points(self):
@@ -869,30 +891,39 @@ class bank(design.design):
# From control signal to the module pin
# Connection from the central bus to the main control block crosses
# pre-decoder and this connection is in metal3
connection = []
connection.append((self.prefix+"clk_buf_bar", self.precharge_array_inst[0].get_pin("en").lc()))
connection.append((self.prefix+"w_en0", self.write_driver_array_inst[0].get_pin("en").lc()))
connection.append((self.prefix+"s_en0", self.sense_amp_array_inst[0].get_pin("en").lc()))
for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos,
rotate=90)
# clk to wordline_driver
control_signal = self.prefix+"clk_buf"
pin_pos = self.wordline_driver_inst[0].get_pin("en").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch)
control_x_offset = self.bus_xoffset[control_signal].x
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
control_via_pos = vector(control_x_offset, mid_pos.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_via_pos,
rotate=90)
write_inst = 0
read_inst = 0
# Control lines for RW ports
for port in range(self.total_ports):
connection = []
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"):
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[read_inst].get_pin("en").lc()))
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"):
connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[write_inst].get_pin("en").lc()))
write_inst += 1
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"):
connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[read_inst].get_pin("en").lc()))
read_inst += 1
for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
self.add_path("metal1", [control_pos, pin_pos])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_pos,
rotate=90)
# clk to wordline_driver
control_signal = self.prefix+"clk_buf{}".format(port)
pin_pos = self.wordline_driver_inst[port].get_pin("en").uc()
mid_pos = pin_pos + vector(0,self.m1_pitch)
control_x_offset = self.bus_xoffset[control_signal].x
control_pos = vector(control_x_offset + self.m1_width, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
control_via_pos = vector(control_x_offset, mid_pos.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=control_via_pos,
rotate=90)
def analytical_delay(self, slew, load):
+47 -16
View File
@@ -15,9 +15,11 @@ class bank_select(design.design):
banks are created in upper level SRAM module
"""
def __init__(self, name="bank_select"):
def __init__(self, name="bank_select", port="rw"):
design.design.__init__(self, name)
self.port = port
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@@ -38,10 +40,17 @@ class bank_select(design.design):
def add_pins(self):
# Number of control lines in the bus
self.num_control_lines = 4
if self.port == "rw":
self.num_control_lines = 4
else:
self.num_control_lines = 3
# The order of the control signals on the control bus:
# FIXME: Update for multiport (these names are not right)
self.input_control_signals = ["clk_buf", "clk_buf_bar", "w_en0", "s_en0"]
self.input_control_signals = ["clk_buf", "clk_buf_bar"]
if (self.port == "rw") or (self.port == "w"):
self.input_control_signals.append("w_en")
if (self.port == "rw") or (self.port == "r"):
self.input_control_signals.append("s_en")
# These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_"+str for str in self.input_control_signals]
@@ -53,16 +62,26 @@ class bank_select(design.design):
def add_modules(self):
""" Create modules for later instantiation """
from importlib import reload
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
self.bitcell = self.mod_bitcell()
height = self.bitcell.height + drc["poly_to_active"]
# 1x Inverter
self.inv = pinv()
self.add_mod(self.inv)
self.inv_sel = pinv(height=height)
self.add_mod(self.inv_sel)
# 4x Inverter
self.inv4x = pinv(4)
self.inv = self.inv4x = pinv(4)
self.add_mod(self.inv4x)
self.nor2 = pnor2()
self.nor2 = pnor2(height=height)
self.add_mod(self.nor2)
self.inv4x_nor = pinv(size=4, height=height)
self.add_mod(self.inv4x_nor)
self.nand2 = pnand2()
self.add_mod(self.nand2)
@@ -83,7 +102,7 @@ class bank_select(design.design):
def create_modules(self):
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
mod=self.inv)
mod=self.inv_sel)
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
self.logic_inst = []
@@ -107,6 +126,14 @@ class bank_select(design.design):
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x_nor))
self.connect_inst([gated_name+"_temp_bar",
gated_name,
"vdd",
"gnd"])
# the rest are AND (nand2+inv) gates
else:
self.logic_inst.append(self.add_inst(name=name_nand,
@@ -117,13 +144,13 @@ class bank_select(design.design):
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x))
self.connect_inst([gated_name+"_temp_bar",
gated_name,
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x))
self.connect_inst([gated_name+"_temp_bar",
gated_name,
"vdd",
"gnd"])
def place_modules(self):
@@ -140,7 +167,11 @@ class bank_select(design.design):
input_name = self.input_control_signals[i]
y_offset = self.inv.height * i
if i == 0:
y_offset = 0
else:
y_offset = self.inv4x_nor.height + self.inv.height * (i-1)
if i%2:
y_offset += self.inv.height
mirror = "MX"
+121 -70
View File
@@ -18,12 +18,19 @@ class control_logic(design.design):
Dynamically generated Control logic for the total SRAM circuit.
"""
def __init__(self, num_rows):
def __init__(self, num_rows, port_type="rw"):
""" Constructor """
design.design.__init__(self, "control_logic")
debug.info(1, "Creating {}".format(self.name))
name = "control_logic_" + port_type
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(name))
self.num_rows = num_rows
self.port_type = port_type
if self.port_type == "rw":
self.num_control_signals = 2
else:
self.num_control_signals = 1
self.create_netlist()
if not OPTS.netlist_only:
@@ -41,7 +48,7 @@ class control_logic(design.design):
self.place_modules()
self.route_all()
self.add_lvs_correspondence_points()
#self.add_lvs_correspondence_points()
self.DRC_LVS()
@@ -61,7 +68,7 @@ class control_logic(design.design):
dff = dff_inv()
dff_height = dff.height
self.ctrl_dff_array = dff_inv_array(rows=2,columns=1)
self.ctrl_dff_array = dff_inv_array(rows=self.num_control_signals,columns=1)
self.add_mod(self.ctrl_dff_array)
self.nand2 = pnand2(height=dff_height)
@@ -81,29 +88,50 @@ class control_logic(design.design):
self.inv8 = pinv(size=16, height=dff_height)
self.add_mod(self.inv8)
from importlib import reload
c = reload(__import__(OPTS.replica_bitline))
replica_bitline = getattr(c, OPTS.replica_bitline)
# FIXME: These should be tuned according to the size!
delay_stages = 4 # Must be non-inverting
delay_fanout = 3 # This can be anything >=2
bitcell_loads = int(math.ceil(self.num_rows / 5.0))
self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads)
self.add_mod(self.replica_bitline)
if (self.port_type == "rw") or (self.port_type == "r"):
from importlib import reload
c = reload(__import__(OPTS.replica_bitline))
replica_bitline = getattr(c, OPTS.replica_bitline)
# FIXME: These should be tuned according to the size!
delay_stages = 4 # Must be non-inverting
delay_fanout = 3 # This can be anything >=2
bitcell_loads = int(math.ceil(self.num_rows / 5.0))
self.replica_bitline = replica_bitline(delay_stages, delay_fanout, bitcell_loads, name="replica_bitline_"+self.port_type)
self.add_mod(self.replica_bitline)
def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """
# List of input control signals
self.input_list =["csb","web0"]
self.dff_output_list =["cs_bar", "cs", "we_bar", "we"]
if self.port_type == "rw":
self.input_list = ["csb", "web"]
else:
self.input_list = ["csb"]
if self.port_type == "rw":
self.dff_output_list = ["cs_bar", "cs", "we_bar", "we"]
else:
self.dff_output_list = ["cs_bar", "cs"]
# list of output control signals (for making a vertical bus)
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"]
if self.port_type == "rw":
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"]
else:
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "cs"]
# leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
# Outputs to the bank
self.output_list = ["s_en0", "w_en0", "clk_buf_bar", "clk_buf"]
if self.port_type == "r":
self.output_list = ["s_en"]
elif self.port_type == "w":
self.output_list = ["w_en"]
else:
self.output_list = ["s_en", "w_en"]
self.output_list.append("clk_buf_bar")
self.output_list.append("clk_buf")
self.supply_list = ["vdd", "gnd"]
@@ -118,14 +146,13 @@ class control_logic(design.design):
def create_modules(self):
""" Create all the modules """
self.create_dffs()
self.create_clk_row()
self.create_we_row()
# self.create_trien_row()
# self.create_trien_bar_row()
self.create_rbl_in_row()
self.create_sen_row()
self.create_rbl()
self.create_clk_row()
if (self.port_type == "rw") or (self.port_type == "w"):
self.create_we_row()
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_rbl_in_row()
self.create_sen_row()
self.create_rbl()
def place_modules(self):
@@ -134,38 +161,44 @@ class control_logic(design.design):
# and add the vdd/gnd pins
self.row_end_inst = []
# Add the control flops on the left of the bus
self.place_dffs()
row = 0
# Add the logic on the right of the bus
self.place_clk_row(row=0) # clk is a double-high cell
self.place_we_row(row=2)
# self.place_trien_row(row=3)
# self.place_trien_bar_row(row=4)
self.place_rbl_in_row(row=3)
self.place_sen_row(row=4)
self.place_rbl(row=5)
self.place_clk_row(row=row) # clk is a double-high cell
row += 2
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_we_row(row=row)
pre_height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.by()
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_rbl_in_row(row=row)
self.place_sen_row(row=row+1)
self.place_rbl(row=row+2)
pre_height = self.rbl_inst.uy()
control_center_y = self.rbl_inst.by()
# This offset is used for placement of the control logic in
# the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.rbl_inst.by())
# This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
# Extra pitch on top and right
self.height = self.rbl_inst.uy() + self.m3_pitch
self.height = pre_height + self.m3_pitch
# Max of modules or logic rows
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch
if (self.port_type == "rw") or (self.port_type == "r"):
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch
else:
self.width = max([inst.rx() for inst in self.row_end_inst]) + self.m2_pitch
def route_all(self):
""" Routing between modules """
self.route_dffs()
#self.route_trien()
#self.route_trien_bar()
self.route_rbl_in()
self.route_wen()
self.route_sen()
if (self.port_type == "rw") or (self.port_type == "w"):
self.route_wen()
if (self.port_type == "rw") or (self.port_type == "r"):
self.route_rbl_in()
self.route_sen()
self.route_clk()
self.route_supply()
@@ -202,7 +235,7 @@ class control_logic(design.design):
def create_rbl_in_row(self):
self.rbl_in_bar_inst=self.add_inst(name="nand3_rbl_in_bar",
self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar",
mod=self.nand2)
self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"])
@@ -238,7 +271,7 @@ class control_logic(design.design):
# input: input: pre_s_en_bar, output: s_en
self.s_en_inst=self.add_inst(name="inv_s_en",
mod=self.inv8)
self.connect_inst(["pre_s_en_bar", "s_en0", "vdd", "gnd"])
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
def place_sen_row(self,row):
"""
@@ -262,7 +295,11 @@ class control_logic(design.design):
def route_dffs(self):
""" Route the input inverters """
dff_out_map = zip(["dout_bar[{}]".format(i) for i in range(3)], ["cs", "we"])
if self.port_type == "r":
control_inputs = ["cs"]
else:
control_inputs = ["cs", "we"]
dff_out_map = zip(["dout_bar[{}]".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs)
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
# Connect the clock rail to the other clock rail
@@ -275,7 +312,8 @@ class control_logic(design.design):
rotate=90)
self.copy_layout_pin(self.ctrl_dff_inst, "din[0]", "csb")
self.copy_layout_pin(self.ctrl_dff_inst, "din[1]", "web0")
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din[1]", "web")
def create_dffs(self):
@@ -302,23 +340,30 @@ class control_logic(design.design):
def create_we_row(self):
# input: WE, CS output: w_en_bar
self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar",
mod=self.nand3)
self.connect_inst(["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"])
if self.port_type == "rw":
nand_mod = self.nand3
temp = ["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]
else:
nand_mod = self.nand2
temp = ["clk_buf_bar", "cs", "w_en_bar", "vdd", "gnd"]
self.w_en_bar_inst = self.add_inst(name="nand3_w_en_bar",
mod=nand_mod)
self.connect_inst(temp)
# input: w_en_bar, output: pre_w_en
self.pre_w_en_inst=self.add_inst(name="inv_pre_w_en",
mod=self.inv1)
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
self.pre_w_en_inst = self.add_inst(name="inv_pre_w_en",
mod=self.inv1)
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
# BUFFER INVERTERS FOR W_EN
self.pre_w_en_bar_inst=self.add_inst(name="inv_pre_w_en_bar",
mod=self.inv2)
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
self.pre_w_en_bar_inst = self.add_inst(name="inv_pre_w_en_bar",
mod=self.inv2)
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
self.w_en_inst=self.add_inst(name="inv_w_en2",
mod=self.inv8)
self.connect_inst(["pre_w_en_bar", "w_en0", "vdd", "gnd"])
self.w_en_inst = self.add_inst(name="inv_w_en2",
mod=self.inv8)
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
def place_we_row(self,row):
@@ -328,7 +373,10 @@ class control_logic(design.design):
w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar_inst.place(offset=w_en_bar_offset,
mirror=mirror)
x_off += self.nand3.width
if self.port_type == "rw":
x_off += self.nand3.width
else:
x_off += self.nand2.width
pre_w_en_offset = vector(x_off, y_off)
self.pre_w_en_inst.place(offset=pre_w_en_offset,
@@ -422,7 +470,10 @@ class control_logic(design.design):
def route_wen(self):
wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"])
if self.port_type == "rw":
wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"])
else:
wen_map = zip(["A", "B"], ["clk_buf_bar", "cs"])
self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets)
# Connect the NAND3 output to the inverter
@@ -435,7 +486,7 @@ class control_logic(design.design):
self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()])
self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()])
self.connect_output(self.w_en_inst, "Z", "w_en0")
self.connect_output(self.w_en_inst, "Z", "w_en")
def route_sen(self):
rbl_out_pos = self.rbl_inst.get_pin("out").bc()
@@ -446,7 +497,7 @@ class control_logic(design.design):
self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()])
self.connect_output(self.s_en_inst, "Z", "s_en0")
self.connect_output(self.s_en_inst, "Z", "s_en")
def route_clk(self):
""" Route the clk and clk_buf_bar signal internally """
@@ -498,9 +549,9 @@ class control_logic(design.design):
self.add_power_pin("gnd", pin_loc)
self.add_path("metal1", [row_loc, pin_loc])
self.copy_layout_pin(self.rbl_inst,"gnd")
self.copy_layout_pin(self.rbl_inst,"vdd")
if (self.port_type == "rw") or (self.port_type == "r"):
self.copy_layout_pin(self.rbl_inst,"gnd")
self.copy_layout_pin(self.rbl_inst,"vdd")
self.copy_layout_pin(self.ctrl_dff_inst,"gnd")
self.copy_layout_pin(self.ctrl_dff_inst,"vdd")
+4
View File
@@ -13,8 +13,12 @@ class delay_chain(design.design):
Usually, this will be constant, but it could have varied fanout.
"""
unique_id = 1
def __init__(self, fanout_list, name="delay_chain"):
"""init function"""
name = name+"_{}".format(delay_chain.unique_id)
delay_chain.unique_id += 1
design.design.__init__(self, name)
# Two fanouts are needed so that we can route the vdd/gnd connections
+56 -28
View File
@@ -81,8 +81,8 @@ class replica_bitline(design.design):
""" Add the modules for later usage """
from importlib import reload
g = reload(__import__(OPTS.delay_chain))
self.mod_delay_chain = getattr(g, OPTS.delay_chain)
#g = reload(__import__(OPTS.delay_chain))
#self.mod_delay_chain = getattr(g, OPTS.delay_chain)
g = reload(__import__(OPTS.replica_bitcell))
self.mod_replica_bitcell = getattr(g, OPTS.replica_bitcell)
@@ -95,7 +95,8 @@ class replica_bitline(design.design):
self.add_mod(self.rbl)
# FIXME: The FO and depth of this should be tuned
self.delay_chain = self.mod_delay_chain([self.delay_fanout]*self.delay_stages)
from delay_chain import delay_chain
self.delay_chain = delay_chain([self.delay_fanout]*self.delay_stages)
self.add_mod(self.delay_chain)
self.inv = pinv()
@@ -110,12 +111,12 @@ class replica_bitline(design.design):
# This is the threshold detect inverter on the output of the RBL
self.rbl_inv_inst=self.add_inst(name="rbl_inv",
mod=self.inv)
self.connect_inst(["bl[0]", "out", "vdd", "gnd"])
self.connect_inst(["bl0[0]", "out", "vdd", "gnd"])
self.tx_inst=self.add_inst(name="rbl_access_tx",
mod=self.access_tx)
# D, G, S, B
self.connect_inst(["vdd", "delayed_en", "bl[0]", "vdd"])
self.connect_inst(["vdd", "delayed_en", "bl0[0]", "vdd"])
# add the well and poly contact
self.dc_inst=self.add_inst(name="delay_chain",
@@ -124,26 +125,33 @@ class replica_bitline(design.design):
self.rbc_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell)
self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"])
temp = []
for port in range(self.total_ports):
temp.append("bl{}[0]".format(port))
temp.append("br{}[0]".format(port))
for port in range(self.total_ports):
temp.append("delayed_en")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
#self.connect_inst(["bl[0]", "br[0]", "delayed_en", "vdd", "gnd"])
self.rbl_inst=self.add_inst(name="load",
mod=self.rbl)
total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
temp = []
temp.append("bl[0]")
temp.append("br[0]")
for port in range(total_ports - 1):
temp.append("gnd")
temp.append("gnd")
for port in range(self.total_ports):
temp.append("bl{}[0]".format(port))
temp.append("br{}[0]".format(port))
for wl in range(self.bitcell_loads):
for port in range(total_ports):
for port in range(self.total_ports):
temp.append("gnd")
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
self.wl_list = self.rbl.cell.list_all_wl_names()
self.bl_list = self.rbl.cell.list_write_bl_names()
def place_modules(self):
""" Add all of the module instances in the logical netlist """
@@ -160,9 +168,6 @@ class replica_bitline(design.design):
mirror="MX")
self.rbl_inst.place(self.rbl_offset)
def route(self):
@@ -178,15 +183,27 @@ class replica_bitline(design.design):
wl = self.wl_list[0]+"[{}]".format(row)
pin = self.rbl_inst.get_pin(wl)
# Route the connection to the right so that it doesn't interfere
# with the cells
# Route the connection to the right so that it doesn't interfere with the cells
# Wordlines may be close to each other when tiled, so gnd connections are routed in opposite directions
if row % 2 == 0:
vertical_extension = vector(0, 1.5*drc["minwidth_metal1"] + 0.5*contact.m1m2.height)
else:
vertical_extension = vector(0, -1.5*drc["minwidth_metal1"] - 1.5*contact.m1m2.height)
pin_right = pin.rc()
pin_extension = pin_right + vector(self.m1_pitch,0)
pin_extension1 = pin_right + vector(self.m3_pitch,0)
pin_extension2 = pin_extension1 + vertical_extension
if pin.layer != "metal1":
continue
self.add_path("metal1", [pin_right, pin_extension])
self.add_power_pin("gnd", pin_extension)
self.add_path("metal1", [pin_right, pin_extension1, pin_extension2])
self.add_power_pin("gnd", pin_extension2)
# for multiport, need to short wordlines to each other so they all connect to gnd
wl_last = self.wl_list[self.total_ports-1]+"[{}]".format(row)
pin_last = self.rbl_inst.get_pin(wl_last)
correct = vector(0.5*drc["minwidth_metal1"], 0)
self.add_path("metal1", [pin.rc()-correct, pin_last.rc()-correct])
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
@@ -243,11 +260,22 @@ class replica_bitline(design.design):
# 3. Route the contact of previous route to the bitcell WL
# route bend of previous net to bitcell WL
wl_offset = self.rbc_inst.get_pin("wl").lc()
xmid_point= 0.5*(wl_offset.x+contact_offset.x)
wl_mid1 = vector(xmid_point,contact_offset.y)
wl_mid2 = vector(xmid_point,wl_offset.y)
self.add_path("metal1", [contact_offset, wl_mid1, wl_mid2, wl_offset])
wl_offset = self.rbc_inst.get_pin(self.wl_list[0]).lc()
wl_mid1 = wl_offset - vector(1.5*drc["minwidth_metal1"], 0)
wl_mid2 = vector(wl_mid1.x, contact_offset.y)
#xmid_point= 0.5*(wl_offset.x+contact_offset.x)
#wl_mid1 = vector(xmid_point,contact_offset.y)
#wl_mid2 = vector(xmid_point,wl_offset.y)
self.add_path("metal1", [wl_offset, wl_mid1, wl_mid2, contact_offset])
# 4. Short wodlines if multiport
wl = self.wl_list[0]
wl_last = self.wl_list[self.total_ports-1]
pin = self.rbc_inst.get_pin(wl)
pin_last = self.rbc_inst.get_pin(wl_last)
correct = vector(0.5*drc["minwidth_metal1"], 0)
self.add_path("metal1", [pin.lc()+correct, pin_last.lc()+correct])
# DRAIN ROUTE
# Route the drain to the vdd rail
@@ -262,7 +290,7 @@ class replica_bitline(design.design):
# Route the connection of the source route to the RBL bitline (left)
# Via will go halfway down from the bitcell
bl_offset = self.rbc_inst.get_pin("bl").bc()
bl_offset = self.rbc_inst.get_pin(self.bl_list[0]).bc()
# Route down a pitch so we can use M2 routing
bl_down_offset = bl_offset - vector(0, self.m2_pitch)
self.add_path("metal2",[source_offset, bl_down_offset, bl_offset])
+82
View File
@@ -0,0 +1,82 @@
import debug
import design
from tech import drc, spice
from vector import vector
from globals import OPTS
from pbitcell import pbitcell
class replica_pbitcell(design.design):
"""
Creates a replica bitcell using pbitcell
"""
def __init__(self):
self.num_rw_ports = OPTS.num_rw_ports
self.num_w_ports = OPTS.num_w_ports
self.num_r_ports = OPTS.num_r_ports
self.total_ports = self.num_rw_ports + self.num_w_ports + self.num_r_ports
design.design.__init__(self, "replica_pbitcell")
debug.info(1, "create a replica bitcell using pbitcell with {0} rw ports, {1} w ports and {2} r ports".format(self.num_rw_ports,
self.num_w_ports,
self.num_r_ports))
self.create_netlist()
self.create_layout()
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_modules()
def create_layout(self):
self.place_pbitcell()
self.route_rbc_connections()
self.DRC_LVS()
def add_pins(self):
for port in range(self.total_ports):
self.add_pin("bl{}".format(port))
self.add_pin("br{}".format(port))
for port in range(self.total_ports):
self.add_pin("wl{}".format(port))
self.add_pin("vdd")
self.add_pin("gnd")
def add_modules(self):
self.prbc = pbitcell(replica_bitcell=True)
self.add_mod(self.prbc)
self.height = self.prbc.height
self.width = self.prbc.width
def create_modules(self):
self.prbc_inst = self.add_inst(name="pbitcell",
mod=self.prbc)
temp = []
for port in range(self.total_ports):
temp.append("bl{}".format(port))
temp.append("br{}".format(port))
for port in range(self.total_ports):
temp.append("wl{}".format(port))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_pbitcell(self):
offset = [0,0]
self.prbc_inst.place(offset=offset)
def route_rbc_connections(self):
for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "bl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "br{}".format(port))
for port in range(self.total_ports):
self.copy_layout_pin(self.prbc_inst, "wl{}".format(port))
self.copy_layout_pin(self.prbc_inst, "vdd")
self.copy_layout_pin(self.prbc_inst, "gnd")