mirror of
https://github.com/VLSIDA/OpenRAM.git
synced 2026-09-02 21:39:03 +02:00
Changes to simplify metal preferred directions and pitches.
Changes to allow decoder height to be a 2x multiple of bitcell height. Split of control logic tests. Fixed track spacing in SRAM and channel router PEP8 cleanup.
This commit is contained in:
+34
-27
@@ -83,10 +83,10 @@ class bank(design.design):
|
||||
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
|
||||
for port in self.write_ports:
|
||||
for bit in range(self.word_size):
|
||||
self.add_pin("din{0}_{1}".format(port,bit), "INPUT")
|
||||
self.add_pin("din{0}_{1}".format(port, bit), "INPUT")
|
||||
for port in self.all_ports:
|
||||
for bit in range(self.addr_size):
|
||||
self.add_pin("addr{0}_{1}".format(port,bit), "INPUT")
|
||||
self.add_pin("addr{0}_{1}".format(port, bit), "INPUT")
|
||||
|
||||
# For more than one bank, we have a bank select and name
|
||||
# the signals gated_*.
|
||||
@@ -128,16 +128,14 @@ class bank(design.design):
|
||||
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
|
||||
# This will ensure the pin is only on the top or bottom edge
|
||||
if port % 2:
|
||||
via_offset = bl_pin.uc() + vector(0, self.m2_pitch)
|
||||
via_offset = bl_pin.uc() + vector(0, 1.5 * self.m2_pitch)
|
||||
left_right_offset = vector(self.max_x_offset, via_offset.y)
|
||||
else:
|
||||
via_offset = bl_pin.bc() - vector(0, self.m2_pitch)
|
||||
via_offset = bl_pin.bc() - vector(0, 1.5 * self.m2_pitch)
|
||||
left_right_offset = vector(self.min_x_offset, via_offset.y)
|
||||
if bl_pin == "m1":
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=via_offset)
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=via_offset)
|
||||
self.add_via_stack_center(from_layer=bl_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=via_offset)
|
||||
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
|
||||
layer="m3",
|
||||
start=left_right_offset,
|
||||
@@ -635,11 +633,10 @@ class bank(design.design):
|
||||
# Port 0
|
||||
# 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[0] - self.m2_pitch, self.min_y_offset)
|
||||
control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - self.m3_pitch, self.min_y_offset)
|
||||
# The control bus is routed up to two pitches below the bitcell array
|
||||
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
|
||||
self.bus_xoffset[0] = self.create_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=control_bus_offset,
|
||||
names=self.control_signals[0],
|
||||
length=control_bus_length,
|
||||
@@ -650,11 +647,10 @@ class bank(design.design):
|
||||
if len(self.all_ports)==2:
|
||||
# The other control bus is routed up to two pitches above the bitcell array
|
||||
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
|
||||
control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
|
||||
control_bus_offset = vector(self.bitcell_array_right + self.m3_pitch,
|
||||
self.max_y_offset - control_bus_length)
|
||||
# The bus for the right port is reversed so that the rbl_wl is closest to the array
|
||||
self.bus_xoffset[1] = self.create_bus(layer="m2",
|
||||
pitch=self.m2_pitch,
|
||||
offset=control_bus_offset,
|
||||
names=list(reversed(self.control_signals[1])),
|
||||
length=control_bus_length,
|
||||
@@ -844,9 +840,9 @@ class bank(design.design):
|
||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||
|
||||
if port % 2:
|
||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0)
|
||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_nonpref_pitch, 0)
|
||||
else:
|
||||
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
||||
offset = self.column_decoder_inst[port].lr() + vector(self.m2_nonpref_pitch, 0)
|
||||
|
||||
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
|
||||
|
||||
@@ -854,7 +850,9 @@ class bank(design.design):
|
||||
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
|
||||
|
||||
route_map = list(zip(decode_pins, column_mux_pins))
|
||||
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
|
||||
self.create_vertical_channel_route(route_map,
|
||||
offset,
|
||||
self.m1_stack)
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
"""
|
||||
@@ -910,30 +908,39 @@ class bank(design.design):
|
||||
# pre-decoder and this connection is in metal3
|
||||
connection = []
|
||||
connection.append((self.prefix + "p_en_bar{}".format(port),
|
||||
self.port_data_inst[port].get_pin("p_en_bar").lc()))
|
||||
self.port_data_inst[port].get_pin("p_en_bar").lc(),
|
||||
self.port_data_inst[port].get_pin("p_en_bar").layer))
|
||||
|
||||
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
|
||||
connection.append((self.prefix + "wl_en{}".format(port),
|
||||
self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
|
||||
self.bitcell_array_inst.get_pin(rbl_wl_name).lc(),
|
||||
self.bitcell_array_inst.get_pin(rbl_wl_name).layer))
|
||||
|
||||
if port in self.write_ports:
|
||||
if port % 2:
|
||||
connection.append((self.prefix + "w_en{}".format(port),
|
||||
self.port_data_inst[port].get_pin("w_en").rc()))
|
||||
self.port_data_inst[port].get_pin("w_en").rc(),
|
||||
self.port_data_inst[port].get_pin("w_en").layer))
|
||||
else:
|
||||
connection.append((self.prefix + "w_en{}".format(port),
|
||||
self.port_data_inst[port].get_pin("w_en").lc()))
|
||||
self.port_data_inst[port].get_pin("w_en").lc(),
|
||||
self.port_data_inst[port].get_pin("w_en").layer))
|
||||
|
||||
if port in self.read_ports:
|
||||
connection.append((self.prefix + "s_en{}".format(port),
|
||||
self.port_data_inst[port].get_pin("s_en").lc()))
|
||||
self.port_data_inst[port].get_pin("s_en").lc(),
|
||||
self.port_data_inst[port].get_pin("s_en").layer))
|
||||
|
||||
for (control_signal, pin_pos) in connection:
|
||||
control_mid_pos = self.bus_xoffset[port][control_signal]
|
||||
control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y)
|
||||
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=control_pos)
|
||||
for (control_signal, pin_pos, pin_layer) in connection:
|
||||
if port==0:
|
||||
y_offset = self.min_y_offset
|
||||
else:
|
||||
y_offset = self.max_y_offset
|
||||
control_pos = vector(self.bus_xoffset[port][control_signal].x, y_offset)
|
||||
if pin_layer == "m1":
|
||||
self.add_wire(self.m1_stack, [control_pos, pin_pos])
|
||||
elif pin_layer == "m3":
|
||||
self.add_wire(self.m2_stack[::-1], [control_pos, pin_pos])
|
||||
|
||||
# clk to wordline_driver
|
||||
control_signal = self.prefix + "wl_en{}".format(port)
|
||||
|
||||
@@ -205,7 +205,7 @@ class bank_select(design.design):
|
||||
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
|
||||
self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=bank_sel_inv_pin.lc())
|
||||
offset=bank_sel_inv_pin.center())
|
||||
|
||||
# Route the pin to the left edge as well
|
||||
bank_sel_pin_pos=vector(0, 0)
|
||||
@@ -242,30 +242,31 @@ class bank_select(design.design):
|
||||
|
||||
# Connect the logic output to inverter input
|
||||
out_pin = logic_inst.get_pin("Z")
|
||||
out_pos = out_pin.rc()
|
||||
out_pos = out_pin.center()
|
||||
in_pin = inv_inst.get_pin("A")
|
||||
in_pos = in_pin.lc()
|
||||
in_pos = in_pin.center()
|
||||
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
|
||||
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
|
||||
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
|
||||
|
||||
# Connect the logic B input to bank_sel / bank_sel_bar
|
||||
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0)
|
||||
logic_pin = logic_inst.get_pin("B")
|
||||
logic_pos = logic_pin.center()
|
||||
input_pos = vector(xoffset_bank_signal, logic_pos.y)
|
||||
self.add_path("m2", [logic_pos, input_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=logic_pos,
|
||||
directions=("H", "H"))
|
||||
self.add_path("m3", [logic_pos, input_pos])
|
||||
self.add_via_center(self.m2_stack,
|
||||
input_pos)
|
||||
self.add_via_stack_center(from_layer=logic_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=logic_pos)
|
||||
|
||||
# Connect the logic A input to the input pin
|
||||
logic_pos = logic_inst.get_pin("A").lc()
|
||||
logic_pin = logic_inst.get_pin("A")
|
||||
logic_pos = logic_pin.center()
|
||||
input_pos = vector(0, logic_pos.y)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=logic_pos,
|
||||
directions=("H", "H"))
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=logic_pos,
|
||||
directions=("H", "H"))
|
||||
self.add_via_stack_center(from_layer=logic_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=logic_pos)
|
||||
self.add_layout_pin_segment_center(text=input_name,
|
||||
layer="m3",
|
||||
start=input_pos,
|
||||
|
||||
@@ -384,7 +384,10 @@ class control_logic(design.design):
|
||||
height = self.control_logic_center.y - self.m2_pitch
|
||||
offset = vector(self.ctrl_dff_array.width, 0)
|
||||
|
||||
self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height)
|
||||
self.rail_offsets = self.create_vertical_bus("m2",
|
||||
offset,
|
||||
self.internal_bus_list,
|
||||
height)
|
||||
|
||||
def create_instances(self):
|
||||
""" Create all the instances """
|
||||
@@ -432,7 +435,7 @@ class control_logic(design.design):
|
||||
row += 1
|
||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||
self.place_rbl_delay_row(row)
|
||||
row += 1
|
||||
row += 1
|
||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||
self.place_sen_row(row)
|
||||
row += 1
|
||||
@@ -522,16 +525,8 @@ class control_logic(design.design):
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=clk_pos)
|
||||
|
||||
# Connect this at the bottom of the buffer
|
||||
out_pos = self.clk_buf_inst.get_pin("Z").center()
|
||||
mid1 = vector(out_pos.x, 2 * self.m2_pitch)
|
||||
mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y)
|
||||
bus_pos = self.rail_offsets["clk_buf"]
|
||||
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
|
||||
# The pin is on M1, so we need another via as well
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=self.clk_buf_inst.get_pin("Z").center())
|
||||
|
||||
self.route_output_to_bus_jogged(self.clk_buf_inst,
|
||||
"clk_buf")
|
||||
self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
|
||||
|
||||
def create_gated_clk_bar_row(self):
|
||||
@@ -541,7 +536,7 @@ class control_logic(design.design):
|
||||
|
||||
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
|
||||
mod=self.and2)
|
||||
self.connect_inst(["cs", "clk_bar", "gated_clk_bar", "vdd", "gnd"])
|
||||
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
|
||||
|
||||
def place_gated_clk_bar_row(self, row):
|
||||
x_offset = self.control_x_offset
|
||||
@@ -554,31 +549,26 @@ class control_logic(design.design):
|
||||
def route_gated_clk_bar(self):
|
||||
clkbuf_map = zip(["A"], ["clk_buf"])
|
||||
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
|
||||
|
||||
|
||||
out_pos = self.clk_bar_inst.get_pin("Z").center()
|
||||
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
|
||||
mid1 = vector(in_pos.x, out_pos.y)
|
||||
self.add_path("m1", [out_pos, mid1, in_pos])
|
||||
in_pos = self.gated_clk_bar_inst.get_pin("A").center()
|
||||
self.add_zjog("m1", out_pos, in_pos)
|
||||
|
||||
# This is the second gate over, so it needs to be on M3
|
||||
clkbuf_map = zip(["A"], ["cs"])
|
||||
clkbuf_map = zip(["B"], ["cs"])
|
||||
self.connect_vertical_bus(clkbuf_map,
|
||||
self.gated_clk_bar_inst,
|
||||
self.rail_offsets,
|
||||
self.m2_stack[::-1])
|
||||
# The pin is on M1, so we need another via as well
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=self.gated_clk_bar_inst.get_pin("A").center())
|
||||
b_pin = self.gated_clk_bar_inst.get_pin("B")
|
||||
self.add_via_stack_center(from_layer=b_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=b_pin.center())
|
||||
|
||||
# This is the second gate over, so it needs to be on M3
|
||||
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
|
||||
self.connect_vertical_bus(clkbuf_map,
|
||||
self.gated_clk_bar_inst,
|
||||
self.rail_offsets,
|
||||
self.m2_stack[::-1])
|
||||
# The pin is on M1, so we need another via as well
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=self.gated_clk_bar_inst.get_pin("Z").center())
|
||||
self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
|
||||
"gated_clk_bar")
|
||||
|
||||
def create_gated_clk_buf_row(self):
|
||||
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
|
||||
@@ -594,7 +584,9 @@ class control_logic(design.design):
|
||||
|
||||
def route_gated_clk_buf(self):
|
||||
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
|
||||
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
|
||||
self.connect_vertical_bus(clkbuf_map,
|
||||
self.gated_clk_buf_inst,
|
||||
self.rail_offsets)
|
||||
|
||||
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
|
||||
self.connect_vertical_bus(clkbuf_map,
|
||||
@@ -602,8 +594,10 @@ class control_logic(design.design):
|
||||
self.rail_offsets,
|
||||
self.m2_stack[::-1])
|
||||
# The pin is on M1, so we need another via as well
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=self.gated_clk_buf_inst.get_pin("Z").center())
|
||||
z_pin = self.gated_clk_buf_inst.get_pin("Z")
|
||||
self.add_via_stack_center(from_layer=z_pin.layer,
|
||||
to_layer="m2",
|
||||
offset=z_pin.center())
|
||||
|
||||
def create_wlen_row(self):
|
||||
# input pre_p_en, output: wl_en
|
||||
@@ -647,11 +641,16 @@ class control_logic(design.design):
|
||||
in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"])
|
||||
self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets)
|
||||
|
||||
out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc()
|
||||
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc()
|
||||
mid1 = vector(out_pos.x, in_pos.y)
|
||||
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
|
||||
|
||||
out_pin = self.p_en_bar_nand_inst.get_pin("Z")
|
||||
out_pos = out_pin.center()
|
||||
in_pin = self.p_en_bar_driver_inst.get_pin("A")
|
||||
in_pos = in_pin.center()
|
||||
mid1 = vector(in_pos.x, out_pos.y)
|
||||
self.add_path(out_pin.layer, [out_pos, mid1, in_pos])
|
||||
self.add_via_stack_center(from_layer=out_pin.layer,
|
||||
to_layer=in_pin.layer,
|
||||
offset=in_pin.center())
|
||||
|
||||
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
|
||||
|
||||
def create_sen_row(self):
|
||||
@@ -704,11 +703,7 @@ class control_logic(design.design):
|
||||
# Connect from delay line
|
||||
# Connect to rail
|
||||
|
||||
rbl_map = zip(["Z"], ["rbl_bl_delay_bar"])
|
||||
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("m3", "via2", "m2"))
|
||||
# The pin is on M1, so we need another via as well
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center())
|
||||
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
|
||||
|
||||
rbl_map = zip(["A"], ["rbl_bl_delay"])
|
||||
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
|
||||
@@ -766,11 +761,12 @@ class control_logic(design.design):
|
||||
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
|
||||
else:
|
||||
dff_out_map = zip(["dout_bar_0"], ["cs"])
|
||||
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("m3", "via2", "m2"))
|
||||
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, self.m2_stack[::-1])
|
||||
|
||||
# Connect the clock rail to the other clock rail
|
||||
# by routing in the supply rail track to avoid channel conflicts
|
||||
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
||||
mid_pos = in_pos + vector(0, 2 * self.m2_pitch)
|
||||
mid_pos = in_pos + vector(0, self.and2.height)
|
||||
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
|
||||
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
@@ -823,10 +819,10 @@ class control_logic(design.design):
|
||||
self.add_path("m1", [row_loc, pin_loc])
|
||||
|
||||
self.copy_layout_pin(self.delay_inst, "gnd")
|
||||
self.copy_layout_pin(self.delay_inst, "vdd")
|
||||
self.copy_layout_pin(self.delay_inst, "vdd")
|
||||
|
||||
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
|
||||
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
|
||||
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
|
||||
|
||||
def add_lvs_correspondence_points(self):
|
||||
""" This adds some points for easier debugging if LVS goes wrong.
|
||||
@@ -1000,3 +996,15 @@ class control_logic(design.design):
|
||||
offset = vector(x_offset, y_offset)
|
||||
inst.place(offset, mirror)
|
||||
return x_offset + inst.width
|
||||
|
||||
def route_output_to_bus_jogged(self, inst, name):
|
||||
# Connect this at the bottom of the buffer
|
||||
out_pos = inst.get_pin("Z").center()
|
||||
mid1 = vector(out_pos.x, out_pos.y - 0.25 * inst.mod.height)
|
||||
mid2 = vector(self.rail_offsets[name].x, mid1.y)
|
||||
bus_pos = self.rail_offsets[name]
|
||||
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
|
||||
# The pin is on M1, so we need another via as well
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=out_pos)
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ class dff_buf(design.design):
|
||||
height=din_pin.height())
|
||||
|
||||
dout_pin = self.inv2_inst.get_pin("Z")
|
||||
mid_pos = dout_pin.center() + vector(self.m1_pitch, 0)
|
||||
mid_pos = dout_pin.center() + vector(self.m1_nonpref_pitch, 0)
|
||||
q_pos = mid_pos - vector(0, self.m2_pitch)
|
||||
self.add_layout_pin_rect_center(text="Q",
|
||||
layer="m2",
|
||||
|
||||
@@ -12,7 +12,7 @@ from sram_factory import factory
|
||||
from vector import vector
|
||||
from globals import OPTS
|
||||
from errors import drc_error
|
||||
from tech import cell_properties
|
||||
from tech import cell_properties, layer
|
||||
|
||||
|
||||
class hierarchical_decoder(design.design):
|
||||
@@ -89,6 +89,7 @@ class hierarchical_decoder(design.design):
|
||||
self.place_pre_decoder()
|
||||
self.place_row_decoder()
|
||||
self.route_inputs()
|
||||
self.route_outputs()
|
||||
self.route_decoder_bus()
|
||||
self.route_vdd_gnd()
|
||||
self.offset_all_coordinates()
|
||||
@@ -103,7 +104,7 @@ class hierarchical_decoder(design.design):
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and2)
|
||||
self.and3 = factory.create(module_type="pand3",
|
||||
height=self.cell_height)
|
||||
height=self.cell_height)
|
||||
self.add_mod(self.and3)
|
||||
|
||||
self.add_decoders()
|
||||
@@ -186,6 +187,13 @@ class hierarchical_decoder(design.design):
|
||||
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
|
||||
# We will place this many final decoders per row
|
||||
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
|
||||
# We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row
|
||||
if self.decoders_per_row == 1:
|
||||
self.decoder_bus_pitch = self.m2_pitch
|
||||
elif self.decoders_per_row == 2:
|
||||
self.decoder_bus_pitch = self.m3_pitch
|
||||
else:
|
||||
debug.error("Insufficient layers for multi-bit height decoder.", -1)
|
||||
|
||||
# Calculates height and width of row-decoder
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
@@ -194,20 +202,21 @@ class hierarchical_decoder(design.design):
|
||||
else:
|
||||
nand_width = self.and3.width
|
||||
nand_inputs = 3
|
||||
self.internal_routing_width = self.m3_pitch * (self.total_number_of_predecoder_outputs + 1)
|
||||
self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1)
|
||||
self.row_decoder_height = self.inv.height * self.num_rows
|
||||
|
||||
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m3_pitch
|
||||
#print(self.decoders_per_row, nand_inputs)
|
||||
#print(decoder_input_wire_height, self.cell_height)
|
||||
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch
|
||||
# print(self.decoders_per_row, nand_inputs)
|
||||
# print(decoder_input_wire_height, self.cell_height)
|
||||
if decoder_input_wire_height > self.cell_height:
|
||||
debug.warning("Cannot fit multi-bit decoder routes per row.")
|
||||
#debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
|
||||
# debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
|
||||
|
||||
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
|
||||
|
||||
self.input_routing_width = (self.num_inputs + 1) * self.m3_pitch
|
||||
# Calculates height and width of hierarchical decoder
|
||||
# Add extra pitch for good measure
|
||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch
|
||||
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch
|
||||
self.width = self.input_routing_width + self.predecoder_width \
|
||||
+ self.internal_routing_width \
|
||||
+ self.decoders_per_row * nand_width + self.inv.width
|
||||
@@ -226,8 +235,7 @@ class hierarchical_decoder(design.design):
|
||||
input_offset=vector(min_x - self.input_routing_width, 0)
|
||||
|
||||
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
|
||||
self.input_bus = self.create_vertical_pin_bus(layer="m3",
|
||||
pitch=self.m3_pitch,
|
||||
self.input_bus = self.create_vertical_pin_bus(layer="m2",
|
||||
offset=input_offset,
|
||||
names=input_bus_names,
|
||||
length=input_height)
|
||||
@@ -276,13 +284,11 @@ class hierarchical_decoder(design.design):
|
||||
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer="m3",
|
||||
offset=input_offset,
|
||||
directions="nonpref")
|
||||
offset=input_offset)
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer="m3",
|
||||
offset=output_offset,
|
||||
directions="nonpref")
|
||||
self.add_path(("m2"), [input_offset, output_offset])
|
||||
offset=output_offset)
|
||||
self.add_path("m3", [input_offset, output_offset])
|
||||
|
||||
def add_pins(self):
|
||||
""" Add the module pins """
|
||||
@@ -424,7 +430,7 @@ class hierarchical_decoder(design.design):
|
||||
"""
|
||||
if (self.num_inputs >= 4):
|
||||
self.place_decoder_and_array()
|
||||
self.route_decoder()
|
||||
|
||||
|
||||
def place_decoder_and_array(self):
|
||||
"""
|
||||
@@ -460,16 +466,28 @@ class hierarchical_decoder(design.design):
|
||||
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
|
||||
mirror=mirror)
|
||||
|
||||
def route_decoder(self):
|
||||
def route_outputs(self):
|
||||
""" Add the pins. """
|
||||
|
||||
for output in range(self.num_outputs):
|
||||
z_pin = self.and_inst[output].get_pin("Z")
|
||||
self.add_layout_pin(text="decode_{0}".format(output),
|
||||
layer="m1",
|
||||
offset=z_pin.ll(),
|
||||
width=z_pin.width(),
|
||||
height=z_pin.height())
|
||||
max_xoffset = max(x.rx() for x in self.and_inst)
|
||||
|
||||
for output_index in range(self.num_outputs):
|
||||
row_remainder = (output_index % self.decoders_per_row)
|
||||
|
||||
and_inst = self.and_inst[output_index]
|
||||
z_pin = and_inst.get_pin("Z")
|
||||
if row_remainder == 0 and self.decoders_per_row > 1:
|
||||
layer = "m3"
|
||||
self.add_via_stack_center(from_layer=z_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=z_pin.center())
|
||||
else:
|
||||
layer = z_pin.layer
|
||||
|
||||
self.add_layout_pin_segment_center(text="decode_{0}".format(output_index),
|
||||
layer=layer,
|
||||
start=z_pin.center(),
|
||||
end=vector(max_xoffset, z_pin.cy()))
|
||||
|
||||
def route_decoder_bus(self):
|
||||
"""
|
||||
@@ -480,8 +498,8 @@ class hierarchical_decoder(design.design):
|
||||
if (self.num_inputs >= 4):
|
||||
# This leaves an offset for the predecoder output jogs
|
||||
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
|
||||
self.predecode_bus = self.create_vertical_pin_bus(layer="m3",
|
||||
pitch=self.m3_pitch,
|
||||
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
|
||||
pitch=self.decoder_bus_pitch,
|
||||
offset=vector(0, 0),
|
||||
names=input_bus_names,
|
||||
length=self.height)
|
||||
@@ -525,22 +543,27 @@ class hierarchical_decoder(design.design):
|
||||
"""
|
||||
output_index = 0
|
||||
|
||||
if "li" in layer:
|
||||
self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]]
|
||||
else:
|
||||
self.decoder_layers = [self.m2_stack[::-1]]
|
||||
debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.")
|
||||
|
||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||
for index_B in self.predec_groups[1]:
|
||||
for index_A in self.predec_groups[0]:
|
||||
# FIXME: convert to connect_bus?
|
||||
if (output_index < self.num_outputs):
|
||||
row_index = math.floor(output_index / self.decoders_per_row)
|
||||
row_remainder = (output_index % self.decoders_per_row)
|
||||
row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("A"),
|
||||
row_offset)
|
||||
output_index,
|
||||
0)
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("B"),
|
||||
row_offset + self.m3_pitch)
|
||||
output_index,
|
||||
1)
|
||||
output_index = output_index + 1
|
||||
|
||||
elif (self.num_inputs > 5):
|
||||
@@ -549,21 +572,21 @@ class hierarchical_decoder(design.design):
|
||||
for index_A in self.predec_groups[0]:
|
||||
# FIXME: convert to connect_bus?
|
||||
if (output_index < self.num_outputs):
|
||||
row_index = math.floor(output_index / self.decoders_per_row)
|
||||
row_remainder = (output_index % self.decoders_per_row)
|
||||
row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m2_pitch
|
||||
predecode_name = "predecode_{}".format(index_A)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("A"),
|
||||
row_offset)
|
||||
output_index,
|
||||
0)
|
||||
predecode_name = "predecode_{}".format(index_B)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("B"),
|
||||
row_offset + self.m2_pitch)
|
||||
output_index,
|
||||
1)
|
||||
predecode_name = "predecode_{}".format(index_C)
|
||||
self.route_predecode_bus_outputs(predecode_name,
|
||||
self.and_inst[output_index].get_pin("C"),
|
||||
row_offset + 2 * self.m2_pitch)
|
||||
output_index,
|
||||
2)
|
||||
output_index = output_index + 1
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
@@ -583,47 +606,56 @@ class hierarchical_decoder(design.design):
|
||||
# The nand and inv are the same height rows...
|
||||
supply_pin = self.and_inst[num].get_pin(pin_name)
|
||||
pin_pos = vector(xoffset, supply_pin.cy())
|
||||
self.add_path("m1",
|
||||
self.add_path(supply_pin.layer,
|
||||
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin_pos)
|
||||
loc=pin_pos,
|
||||
start_layer=supply_pin.layer)
|
||||
|
||||
# Copy the pins from the predecoders
|
||||
for pre in self.pre2x4_inst + self.pre3x8_inst:
|
||||
self.copy_layout_pin(pre, "vdd")
|
||||
self.copy_layout_pin(pre, "gnd")
|
||||
|
||||
def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
|
||||
def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index):
|
||||
"""
|
||||
Connect the routing rail to the given metal1 pin
|
||||
using a routing track at the given y_offset
|
||||
|
||||
"""
|
||||
pin_pos = pin.center()
|
||||
# If we have a single decoder per row, we can route on M1
|
||||
if self.decoders_per_row == 1:
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
self.add_path("m2", [rail_pos, pin_pos])
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m2",
|
||||
offset=pin_pos,
|
||||
directions=("H", "H"))
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer="m3",
|
||||
offset=rail_pos,
|
||||
directions="nonpref")
|
||||
# If not, we must route over the decoder cells on M3
|
||||
else:
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
||||
mid_pos = vector(pin_pos.x, rail_pos.y)
|
||||
self.add_wire(self.m2_stack, [rail_pos, mid_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=rail_pos)
|
||||
self.add_via_stack_center(from_layer=pin_pos.layer,
|
||||
to_layer="m3",
|
||||
offset=pin_pos,
|
||||
directions="nonpref")
|
||||
|
||||
row_index = math.floor(output_index / self.decoders_per_row)
|
||||
row_remainder = (output_index % self.decoders_per_row)
|
||||
row_offset = row_index * self.and_inst[0].height
|
||||
|
||||
pin_pos = pin.center()
|
||||
|
||||
# y_offset is the same for both the M2 and M4 routes so that the rail
|
||||
# contacts align and don't cause problems
|
||||
if pin_index == 0:
|
||||
# Bottom pitch
|
||||
y_offset = row_offset
|
||||
elif pin_index == 1:
|
||||
# One pitch from top
|
||||
y_offset = row_offset + self.and_inst[0].height - self.m3_pitch
|
||||
elif pin_index == 2:
|
||||
# One pitch from bottom
|
||||
y_offset = row_offset + self.m3_pitch
|
||||
else:
|
||||
debug.error("Invalid decoder pitch.")
|
||||
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
|
||||
mid_pos = vector(pin_pos.x, rail_pos.y)
|
||||
self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos])
|
||||
|
||||
self.add_via_stack_center(from_layer="m2",
|
||||
to_layer=self.decoder_layers[row_remainder][0],
|
||||
offset=rail_pos)
|
||||
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer=self.decoder_layers[row_remainder][2],
|
||||
offset=pin_pos,
|
||||
directions=("H", "H"))
|
||||
|
||||
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
|
||||
"""
|
||||
Connect the routing rail to the given metal1 pin using a jog
|
||||
@@ -631,15 +663,21 @@ class hierarchical_decoder(design.design):
|
||||
"""
|
||||
# This routes the pin up to the rail, basically, to avoid conflicts.
|
||||
# It would be fixed with a channel router.
|
||||
# pin_pos = pin.center()
|
||||
# mid_point1 = vector(x_offset, pin_pos.y)
|
||||
# mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
|
||||
# rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||
# self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||
|
||||
pin_pos = pin.center()
|
||||
mid_point1 = vector(x_offset, pin_pos.y)
|
||||
mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
|
||||
self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
|
||||
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
|
||||
self.add_path("m1", [pin_pos, rail_pos])
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m1",
|
||||
offset=pin_pos)
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m3",
|
||||
offset=rail_pos,
|
||||
directions="nonpref")
|
||||
to_layer="m2",
|
||||
offset=rail_pos)
|
||||
|
||||
def input_load(self):
|
||||
if self.determine_predecodes(self.num_inputs)[1]==0:
|
||||
|
||||
@@ -11,6 +11,7 @@ import math
|
||||
import contact
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from tech import cell_properties
|
||||
|
||||
|
||||
class hierarchical_predecode(design.design):
|
||||
@@ -19,7 +20,17 @@ class hierarchical_predecode(design.design):
|
||||
"""
|
||||
def __init__(self, name, input_number, height=None):
|
||||
self.number_of_inputs = input_number
|
||||
self.cell_height = height
|
||||
|
||||
if not height:
|
||||
b = factory.create(module_type="bitcell")
|
||||
try:
|
||||
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
|
||||
except AttributeError:
|
||||
self.cell_multiple = 1
|
||||
self.cell_height = self.cell_multiple * b.height
|
||||
else:
|
||||
self.cell_height = height
|
||||
|
||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||
design.design.__init__(self, name)
|
||||
|
||||
@@ -57,10 +68,10 @@ class hierarchical_predecode(design.design):
|
||||
self.height = self.number_of_outputs * self.and_mod.height
|
||||
|
||||
# x offset for input inverters
|
||||
self.x_off_inv_1 = self.number_of_inputs*self.m3_pitch
|
||||
self.x_off_inv_1 = self.number_of_inputs * self.m2_pitch + self.m2_space
|
||||
|
||||
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
|
||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m3_pitch
|
||||
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.m2_pitch
|
||||
|
||||
# x offset to output inverters
|
||||
self.width = self.x_off_and + self.and_mod.width
|
||||
@@ -68,9 +79,8 @@ class hierarchical_predecode(design.design):
|
||||
def route_rails(self):
|
||||
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
|
||||
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
offset = vector(0.5 * self.m3_width, self.m1_pitch)
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="m3",
|
||||
pitch=self.m3_pitch,
|
||||
offset = vector(0.5 * self.m2_width, self.m3_pitch)
|
||||
self.input_rails = self.create_vertical_pin_bus(layer="m2",
|
||||
offset=offset,
|
||||
names=input_names,
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
@@ -78,9 +88,8 @@ class hierarchical_predecode(design.design):
|
||||
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
|
||||
decode_names = invert_names + non_invert_names
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m3_pitch, self.m1_pitch)
|
||||
self.decode_rails = self.create_vertical_bus(layer="m3",
|
||||
pitch=self.m3_pitch,
|
||||
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m3_pitch)
|
||||
self.decode_rails = self.create_vertical_bus(layer="m2",
|
||||
offset=offset,
|
||||
names=decode_names,
|
||||
length=self.height - 2 * self.m1_pitch)
|
||||
@@ -151,11 +160,13 @@ class hierarchical_predecode(design.design):
|
||||
a_pin = "A_{}".format(num)
|
||||
in_pos = vector(self.input_rails[in_pin].x, y_offset)
|
||||
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
|
||||
self.add_path("m2", [in_pos, a_pos])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=[self.input_rails[in_pin].x, y_offset])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||
self.add_path("m1", [in_pos, a_pos])
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=[self.input_rails[in_pin].x, y_offset])
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=[self.decode_rails[a_pin].x, y_offset])
|
||||
|
||||
def route_output_and(self):
|
||||
"""
|
||||
@@ -188,21 +199,20 @@ class hierarchical_predecode(design.design):
|
||||
rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
|
||||
self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
|
||||
self.add_via_stack_center(from_layer=inv_out_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=rail_pos,
|
||||
directions="nonpref")
|
||||
to_layer="m2",
|
||||
offset=rail_pos)
|
||||
|
||||
# route input
|
||||
pin = self.in_inst[inv_num].get_pin("A")
|
||||
inv_in_pos = pin.lc()
|
||||
in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y)
|
||||
self.add_path("m2", [in_pos, inv_in_pos])
|
||||
self.add_path("m1", [in_pos, inv_in_pos])
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m1",
|
||||
offset=inv_in_pos)
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=inv_in_pos,
|
||||
directions="nonpref")
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=in_pos)
|
||||
offset=in_pos)
|
||||
|
||||
def route_and_to_rails(self):
|
||||
# This 2D array defines the connection mapping
|
||||
@@ -221,14 +231,19 @@ class hierarchical_predecode(design.design):
|
||||
pin = self.and_inst[k].get_pin(gate_pin)
|
||||
pin_pos = pin.center()
|
||||
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
|
||||
self.add_path("m2", [rail_pos, pin_pos])
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=rail_pos,
|
||||
directions="nonpref")
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
self.add_path("m1", [rail_pos, pin_pos])
|
||||
self.add_via_stack_center(from_layer="m1",
|
||||
to_layer="m2",
|
||||
offset=rail_pos)
|
||||
if gate_pin == "A":
|
||||
direction = None
|
||||
else:
|
||||
direction = ("H", "H")
|
||||
|
||||
self.add_via_stack_center(from_layer=pin.layer,
|
||||
to_layer="m1",
|
||||
offset=pin_pos,
|
||||
directions=("H", "H"))
|
||||
directions=direction)
|
||||
|
||||
def route_vdd_gnd(self):
|
||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||
@@ -243,14 +258,16 @@ class hierarchical_predecode(design.design):
|
||||
for n in ["vdd", "gnd"]:
|
||||
and_pin = self.and_inst[num].get_pin(n)
|
||||
supply_offset = and_pin.ll().scale(0, 1)
|
||||
self.add_rect(layer="m1",
|
||||
self.add_rect(layer=and_pin.layer,
|
||||
offset=supply_offset,
|
||||
width=self.and_inst[num].rx())
|
||||
|
||||
# Add pins in two locations
|
||||
for xoffset in [in_xoffset]:
|
||||
pin_pos = vector(xoffset, and_pin.cy())
|
||||
self.add_power_pin(n, pin_pos)
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin_pos,
|
||||
start_layer=and_pin.layer)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
# Copyright (c) 2016-2019 Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
import sys
|
||||
from tech import drc, parameter
|
||||
from math import log
|
||||
import debug
|
||||
import design
|
||||
@@ -13,6 +11,7 @@ from vector import vector
|
||||
|
||||
from globals import OPTS
|
||||
|
||||
|
||||
class port_address(design.design):
|
||||
"""
|
||||
Create the address port (row decoder and wordline driver)..
|
||||
@@ -25,17 +24,16 @@ class port_address(design.design):
|
||||
self.addr_size = int(log(self.num_rows, 2))
|
||||
|
||||
if name == "":
|
||||
name = "port_address_{0}_{1}".format(cols,rows)
|
||||
name = "port_address_{0}_{1}".format(cols, rows)
|
||||
design.design.__init__(self, name)
|
||||
debug.info(2, "create data port of cols {0} rows {1}".format(cols,rows))
|
||||
debug.info(2, "create data port of cols {0} rows {1}".format(cols, rows))
|
||||
|
||||
self.create_netlist()
|
||||
if not OPTS.netlist_only:
|
||||
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
|
||||
debug.check(len(self.all_ports) <= 2, "Bank layout cannot handle more than two ports.")
|
||||
self.create_layout()
|
||||
self.add_boundary()
|
||||
|
||||
|
||||
def create_netlist(self):
|
||||
self.add_pins()
|
||||
self.add_modules()
|
||||
@@ -51,16 +49,15 @@ class port_address(design.design):
|
||||
""" Adding pins for port address module"""
|
||||
|
||||
for bit in range(self.addr_size):
|
||||
self.add_pin("addr_{0}".format(bit),"INPUT")
|
||||
self.add_pin("addr_{0}".format(bit), "INPUT")
|
||||
|
||||
self.add_pin("wl_en", "INPUT")
|
||||
|
||||
for bit in range(self.num_rows):
|
||||
self.add_pin("wl_{0}".format(bit),"OUTPUT")
|
||||
self.add_pin("wl_{0}".format(bit), "OUTPUT")
|
||||
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def route_layout(self):
|
||||
""" Create routing amoung the modules """
|
||||
@@ -71,8 +68,8 @@ class port_address(design.design):
|
||||
def route_supplies(self):
|
||||
""" Propagate all vdd/gnd pins up to this level for all modules """
|
||||
for inst in self.insts:
|
||||
self.copy_power_pins(inst,"vdd")
|
||||
self.copy_power_pins(inst,"gnd")
|
||||
self.copy_power_pins(inst, "vdd")
|
||||
self.copy_power_pins(inst, "gnd")
|
||||
|
||||
def route_pins(self):
|
||||
for row in range(self.addr_size):
|
||||
@@ -119,12 +116,10 @@ class port_address(design.design):
|
||||
temp.extend(["vdd", "gnd"])
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
|
||||
def create_wordline_driver(self):
|
||||
""" Create the Wordline Driver """
|
||||
|
||||
self.wordline_driver_inst = self.add_inst(name="wordline_driver",
|
||||
self.wordline_driver_inst = self.add_inst(name="wordline_driver",
|
||||
mod=self.wordline_driver)
|
||||
|
||||
temp = []
|
||||
@@ -137,19 +132,13 @@ class port_address(design.design):
|
||||
temp.append("gnd")
|
||||
self.connect_inst(temp)
|
||||
|
||||
|
||||
|
||||
def place_instances(self):
|
||||
"""
|
||||
Compute the offsets and place the instances.
|
||||
"""
|
||||
|
||||
# A space for wells or jogging m2
|
||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
|
||||
3*self.m2_pitch)
|
||||
|
||||
row_decoder_offset = vector(0,0)
|
||||
wordline_driver_offset = vector(self.row_decoder.width + self.m2_gap,0)
|
||||
row_decoder_offset = vector(0, 0)
|
||||
wordline_driver_offset = vector(self.row_decoder.width, 0)
|
||||
|
||||
self.wordline_driver_inst.place(wordline_driver_offset)
|
||||
self.row_decoder_inst.place(row_decoder_offset)
|
||||
|
||||
@@ -694,7 +694,7 @@ class port_data(design.design):
|
||||
|
||||
# Channel route each mux separately since we don't minimize the number
|
||||
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
||||
offset = bot_inst_group.inst.ul() + vector(0, self.m1_pitch)
|
||||
offset = bot_inst_group.inst.ul() + vector(0, self.m1_nonpref_pitch)
|
||||
for bit in range(num_bits):
|
||||
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
|
||||
top_names = self._get_bitline_pins(top_inst_group, bit)
|
||||
|
||||
@@ -354,32 +354,30 @@ class replica_bitcell_array(design.design):
|
||||
width=self.width,
|
||||
height=pin.height())
|
||||
|
||||
|
||||
# Replica bitlines
|
||||
for port in range(self.left_rbl+self.right_rbl):
|
||||
for port in range(self.left_rbl + self.right_rbl):
|
||||
inst = self.replica_col_inst[port]
|
||||
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(),self.replica_bl_names[port]):
|
||||
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bl_names[port]):
|
||||
pin = inst.get_pin(pin_name)
|
||||
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
|
||||
name = bl_name
|
||||
else:
|
||||
name = "rbl_{0}_{1}".format(pin_name,port)
|
||||
name = "rbl_{0}_{1}".format(pin_name, port)
|
||||
self.add_layout_pin(text=name,
|
||||
layer=pin.layer,
|
||||
offset=pin.ll().scale(1,0),
|
||||
offset=pin.ll().scale(1, 0),
|
||||
width=pin.width(),
|
||||
height=self.height)
|
||||
|
||||
|
||||
for pin_name in ["vdd","gnd"]:
|
||||
for pin_name in ["vdd", "gnd"]:
|
||||
for inst in self.insts:
|
||||
pin_list = inst.get_pins(pin_name)
|
||||
for pin in pin_list:
|
||||
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
|
||||
self.add_power_pin(name=pin_name,
|
||||
loc=pin.center(),
|
||||
directions=("V", "V"),
|
||||
start_layer=pin.layer)
|
||||
|
||||
|
||||
|
||||
|
||||
def get_rbl_wl_name(self, port):
|
||||
""" Return the WL for the given RBL port """
|
||||
return self.rbl_wl_names[port]
|
||||
@@ -398,7 +396,7 @@ class replica_bitcell_array(design.design):
|
||||
|
||||
# Dynamic Power from Bitline
|
||||
bl_wire = self.gen_bl_wire()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
cell_load = 2 * bl_wire.return_input_cap()
|
||||
bl_swing = OPTS.rbl_delay_percentage
|
||||
freq = spice["default_event_frequency"]
|
||||
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
|
||||
|
||||
@@ -130,13 +130,13 @@ class sense_amp_array(design.design):
|
||||
self.add_power_pin(name="gnd",
|
||||
loc=gnd_pin.center(),
|
||||
start_layer=gnd_pin.layer,
|
||||
vertical=True)
|
||||
directions=("V", "V"))
|
||||
|
||||
vdd_pin = inst.get_pin("vdd")
|
||||
self.add_power_pin(name="vdd",
|
||||
loc=vdd_pin.center(),
|
||||
start_layer=vdd_pin.layer,
|
||||
vertical=True)
|
||||
directions=("V", "V"))
|
||||
|
||||
bl_pin = inst.get_pin(inst.mod.get_bl_names())
|
||||
br_pin = inst.get_pin(inst.mod.get_br_names())
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import debug
|
||||
import design
|
||||
import math
|
||||
import contact
|
||||
from tech import drc
|
||||
from vector import vector
|
||||
from sram_factory import factory
|
||||
from globals import OPTS
|
||||
@@ -120,9 +120,11 @@ class wordline_driver(design.design):
|
||||
self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows)
|
||||
|
||||
def place_drivers(self):
|
||||
and2_xoffset = 2 * self.m1_width + 5 * self.m1_space
|
||||
|
||||
# Leave a well gap to separate the bitcell array well from this well
|
||||
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
|
||||
|
||||
self.width = and2_xoffset + self.decoders_per_row * self.and2.width
|
||||
self.width = self.decoders_per_row * self.and2.width + well_gap
|
||||
self.height = self.and2.height * self.driver_rows
|
||||
|
||||
for inst_index in range(self.bitcell_rows):
|
||||
@@ -136,7 +138,7 @@ class wordline_driver(design.design):
|
||||
y_offset = self.and2.height * row
|
||||
inst_mirror = "R0"
|
||||
|
||||
x_offset = and2_xoffset + dec * self.and2.width
|
||||
x_offset = dec * self.and2.width
|
||||
and2_offset = [x_offset, y_offset]
|
||||
|
||||
# add and2
|
||||
@@ -147,32 +149,26 @@ class wordline_driver(design.design):
|
||||
""" Route all of the signals """
|
||||
|
||||
# Wordline enable connection
|
||||
en_offset = [self.m1_width + 2 * self.m1_space, 0]
|
||||
en_pin = self.and_inst[0].get_pin("B")
|
||||
en_bottom_pos = vector(en_pin.lx(), 0)
|
||||
en_pin = self.add_layout_pin(text="en",
|
||||
layer="m2",
|
||||
offset=en_offset,
|
||||
width=self.m2_width,
|
||||
offset=en_bottom_pos,
|
||||
height=self.height)
|
||||
|
||||
for inst_index in range(self.bitcell_rows):
|
||||
and_inst = self.and_inst[inst_index]
|
||||
row = math.floor(inst_index / self.decoders_per_row)
|
||||
dec = inst_index % self.decoders_per_row
|
||||
|
||||
# en connection
|
||||
b_pin = and_inst.get_pin("B")
|
||||
b_pos = b_pin.center()
|
||||
clk_offset = vector(en_pin.bc().x, b_pos.y)
|
||||
self.add_segment_center(layer="m2",
|
||||
start=clk_offset,
|
||||
end=b_pos)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=b_pos)
|
||||
|
||||
# Drop a via
|
||||
b_pin = and_inst.get_pin("B")
|
||||
self.add_via_stack_center(from_layer=b_pin.layer,
|
||||
to_layer="m2",
|
||||
offset=b_pin.center())
|
||||
|
||||
# connect the decoder input pin to and2 A
|
||||
a_pin = and_inst.get_pin("A")
|
||||
a_pos = a_pin.center()
|
||||
a_offset = vector(clk_offset.x, a_pos.y)
|
||||
# must under the clk line in M1
|
||||
self.add_layout_pin_segment_center(text="in_{0}".format(row),
|
||||
layer="m1",
|
||||
|
||||
@@ -165,7 +165,7 @@ class write_driver_array(design.design):
|
||||
for pin in pin_list:
|
||||
self.add_power_pin(name=n,
|
||||
loc=pin.center(),
|
||||
vertical=True,
|
||||
directions=("V", "V"),
|
||||
start_layer=pin.layer)
|
||||
if self.write_size:
|
||||
for bit in range(self.num_wmasks):
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
# (acting for and on behalf of Oklahoma State University)
|
||||
# All rights reserved.
|
||||
#
|
||||
from math import log
|
||||
import design
|
||||
from tech import drc
|
||||
import debug
|
||||
from sram_factory import factory
|
||||
from vector import vector
|
||||
@@ -43,33 +41,28 @@ class write_mask_and_array(design.design):
|
||||
self.add_pins()
|
||||
self.create_and2_array()
|
||||
|
||||
|
||||
def create_layout(self):
|
||||
self.place_and2_array()
|
||||
spacing = self.wmask_en_len - self.and2.width
|
||||
self.width = (self.num_wmasks*self.and2.width) + ((self.num_wmasks-1)*spacing)
|
||||
self.height = self.and2.height
|
||||
self.add_layout_pins()
|
||||
self.add_boundary()
|
||||
self.DRC_LVS()
|
||||
|
||||
def add_pins(self):
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("wmask_in_{}".format(bit),"INPUT")
|
||||
self.add_pin("wmask_in_{}".format(bit), "INPUT")
|
||||
self.add_pin("en", "INPUT")
|
||||
for bit in range(self.num_wmasks):
|
||||
self.add_pin("wmask_out_{}".format(bit),"OUTPUT")
|
||||
self.add_pin("vdd","POWER")
|
||||
self.add_pin("gnd","GROUND")
|
||||
self.add_pin("wmask_out_{}".format(bit), "OUTPUT")
|
||||
self.add_pin("vdd", "POWER")
|
||||
self.add_pin("gnd", "GROUND")
|
||||
|
||||
def add_modules(self):
|
||||
# Size the AND gate for the number of write drivers it drives, which is equal to the write size.
|
||||
# Assume stage effort of 3 to compute the size
|
||||
self.and2 = factory.create(module_type="pand2",
|
||||
size=self.write_size/4.0)
|
||||
size=self.write_size / 4.0)
|
||||
self.add_mod(self.and2)
|
||||
|
||||
|
||||
def create_and2_array(self):
|
||||
self.and2_insts = {}
|
||||
for bit in range(self.num_wmasks):
|
||||
@@ -81,7 +74,6 @@ class write_mask_and_array(design.design):
|
||||
"wmask_out_{}".format(bit),
|
||||
"vdd", "gnd"])
|
||||
|
||||
|
||||
def place_and2_array(self):
|
||||
# Place the write mask AND array at the start of each write driver enable length.
|
||||
# This ensures the write mask AND array will be directly under the corresponding write driver enable wire.
|
||||
@@ -96,56 +88,45 @@ class write_mask_and_array(design.design):
|
||||
|
||||
self.wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
|
||||
debug.check(self.wmask_en_len >= self.and2.width,
|
||||
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,self.wmask_en_len))
|
||||
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,
|
||||
self.wmask_en_len))
|
||||
|
||||
self.width = self.bitcell.width * self.columns
|
||||
self.height = self.and2.height
|
||||
|
||||
for i in range(self.num_wmasks):
|
||||
base = vector(i * self.wmask_en_len, 0)
|
||||
self.and2_insts[i].place(base)
|
||||
|
||||
|
||||
def add_layout_pins(self):
|
||||
|
||||
# Create the enable pin that connects all write mask AND array's B pins
|
||||
beg_en_pin = self.and2_insts[0].get_pin("B")
|
||||
end_en_pin = self.and2_insts[self.num_wmasks-1].get_pin("B")
|
||||
if self.port % 2:
|
||||
# Extend metal3 to edge of AND array in multiport
|
||||
en_to_edge = self.and2.width - beg_en_pin.cx()
|
||||
self.add_layout_pin(text="en",
|
||||
layer="m3",
|
||||
offset=beg_en_pin.bc(),
|
||||
width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge)
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
|
||||
else:
|
||||
self.add_layout_pin(text="en",
|
||||
layer="m3",
|
||||
offset=beg_en_pin.bc(),
|
||||
width=end_en_pin.cx() - beg_en_pin.cx())
|
||||
en_pin = self.and2_insts[0].get_pin("B")
|
||||
self.add_layout_pin_segment_center(text="en",
|
||||
layer="m3",
|
||||
start=vector(0, en_pin.cy()),
|
||||
end=vector(self.width, en_pin.cy()))
|
||||
|
||||
for i in range(self.num_wmasks):
|
||||
# Copy remaining layout pins
|
||||
self.copy_layout_pin(self.and2_insts[i],"A","wmask_in_{0}".format(i))
|
||||
self.copy_layout_pin(self.and2_insts[i],"Z","wmask_out_{0}".format(i))
|
||||
self.copy_layout_pin(self.and2_insts[i], "A", "wmask_in_{0}".format(i))
|
||||
self.copy_layout_pin(self.and2_insts[i], "Z", "wmask_out_{0}".format(i))
|
||||
|
||||
# Add via connections to metal3 for AND array's B pin
|
||||
en_pin = self.and2_insts[i].get_pin("B")
|
||||
self.add_via_center(layers=self.m1_stack,
|
||||
offset=en_pin.center())
|
||||
self.add_via_center(layers=self.m2_stack,
|
||||
offset=en_pin.center())
|
||||
en_pos = en_pin.center()
|
||||
self.add_via_stack_center(from_layer=en_pin.layer,
|
||||
to_layer="m3",
|
||||
offset=en_pos)
|
||||
|
||||
for supply in ["gnd", "vdd"]:
|
||||
supply_pin=self.and2_insts[i].get_pin(supply)
|
||||
self.add_power_pin(supply, supply_pin.center())
|
||||
|
||||
|
||||
for supply in ["gnd", "vdd"]:
|
||||
supply_pin_left = self.and2_insts[0].get_pin(supply)
|
||||
supply_pin_right = self.and2_insts[self.num_wmasks-1].get_pin(supply)
|
||||
self.add_path("m1",[supply_pin_left.lc(), supply_pin_right.rc()])
|
||||
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
|
||||
self.add_path("m1", [supply_pin_left.lc(), supply_pin_right.rc()])
|
||||
|
||||
def get_cin(self):
|
||||
"""Get the relative capacitance of all the input connections in the bank"""
|
||||
|
||||
Reference in New Issue
Block a user