Merge branch 'dev' of github.com:VLSIDA/PrivateRAM into dev

This commit is contained in:
mrg 2020-07-23 14:17:52 -07:00
commit 317662d4aa
8 changed files with 382 additions and 215 deletions

View File

@ -12,6 +12,69 @@ from vector import vector
import design import design
class channel_net():
def __init__(self, net_name, pins, vertical):
self.name = net_name
self.pins = pins
self.vertical = vertical
# Keep track of the internval
if vertical:
self.min_value = min(i.by() for i in pins)
self.max_value = max(i.uy() for i in pins)
else:
self.min_value = min(i.lx() for i in pins)
self.max_value = max(i.rx() for i in pins)
# Keep track of the conflicts
self.conflicts = []
def __str__(self):
return self.name
def __repr__(self):
return self.name
def __lt__(self, other):
return self.min_value < other.min_value
def pin_overlap(self, pin1, pin2, pitch):
""" Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin,
# for example, so the extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
# Pin 1 must be in the "LEFT" set
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
return overlaps
def pins_overlap(self, other, pitch):
"""
Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps.
"""
for pin1 in self.pins:
for pin2 in other.pins:
if self.pin_overlap(pin1, pin2, pitch):
return True
return False
def segment_overlap(self, other):
"""
Check if the horizontal span of the two nets overlaps eachother.
"""
min_overlap = self.min_value >= other.min_value and self.min_value <= other.max_value
max_overlap = self.max_value >= other.min_value and self.max_value <= other.max_value
return min_overlap or max_overlap
class channel_route(design.design): class channel_route(design.design):
unique_id = 0 unique_id = 0
@ -21,7 +84,8 @@ class channel_route(design.design):
offset, offset,
layer_stack, layer_stack,
directions=None, directions=None,
vertical=False): vertical=False,
parent=None):
""" """
The net list is a list of the nets with each net being a list of pins The net list is a list of the nets with each net being a list of pins
to be connected. The offset is the lower-left of where the to be connected. The offset is the lower-left of where the
@ -40,6 +104,8 @@ class channel_route(design.design):
self.layer_stack = layer_stack self.layer_stack = layer_stack
self.directions = directions self.directions = directions
self.vertical = vertical self.vertical = vertical
# For debugging...
self.parent = parent
if not directions or directions == "pref": if not directions or directions == "pref":
# Use the preferred layer directions # Use the preferred layer directions
@ -86,114 +152,139 @@ class channel_route(design.design):
# FIXME: This is O(n^2), so maybe optimize it. # FIXME: This is O(n^2), so maybe optimize it.
for other_pin, conflicts in g.items(): for other_pin, conflicts in g.items():
if pin in conflicts: if pin in conflicts:
conflicts.remove(pin) g[other_pin].remove(pin)
g[other_pin]=conflicts
return g return g
def route(self):
# Create names for the nets for the graphs
nets = []
index = 0
# print(self.netlist)
for pin_list in self.netlist:
nets.append(channel_net("n{}".format(index), pin_list, self.vertical))
index += 1
def vcg_nets_overlap(self, net1, net2): # Create the (undirected) horizontal constraint graph
""" hcg = collections.OrderedDict()
Check all the pin pairs on two nets and return a pin for net1 in nets:
overlap if any pin overlaps. for net2 in nets:
""" if net1.name == net2.name:
continue
if net1.segment_overlap(net2):
try:
hcg[net1.name].add(net2.name)
except KeyError:
hcg[net1.name] = set([net2.name])
try:
hcg[net2.name].add(net1.name)
except KeyError:
hcg[net2.name] = set([net1.name])
# Initialize the vertical conflict graph (vcg)
# and make a list of all pins
vcg = collections.OrderedDict()
# print("Nets:")
# for net_name in nets:
# print(net_name, [x.name for x in nets[net_name]])
# Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now
if self.vertical: if self.vertical:
pitch = self.horizontal_nonpref_pitch pitch = self.horizontal_nonpref_pitch
else: else:
pitch = self.vertical_nonpref_pitch pitch = self.vertical_nonpref_pitch
for pin1 in net1: for net in nets:
for pin2 in net2: vcg[net.name] = set()
if self.vcg_pin_overlap(pin1, pin2, pitch):
return True
return False for net1 in nets:
for net2 in nets:
def route(self):
# FIXME: Must extend this to a horizontal conflict graph
# too if we want to minimize the
# number of tracks!
# hcg = {}
# Initialize the vertical conflict graph (vcg)
# and make a list of all pins
vcg = collections.OrderedDict()
# Create names for the nets for the graphs
nets = collections.OrderedDict()
index = 0
# print(netlist)
for pin_list in self.netlist:
net_name = "n{}".format(index)
index += 1
nets[net_name] = pin_list
# print("Nets:")
# for net_name in nets:
# print(net_name, [x.name for x in nets[net_name]])
# Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now
for net_name1 in nets:
if net_name1 not in vcg.keys():
vcg[net_name1] = []
for net_name2 in nets:
if net_name2 not in vcg.keys():
vcg[net_name2] = []
# Skip yourself # Skip yourself
if net_name1 == net_name2: if net1.name == net2.name:
continue continue
if self.vcg_nets_overlap(nets[net_name1],
nets[net_name2]): if net1.pins_overlap(net2, pitch):
vcg[net_name2].append(net_name1) vcg[net2.name].add(net1.name)
current_offset = self.offset # Check if there are any cycles net1 <---> net2 in the VCG
# list of routes to do
while vcg: # Some of the pins may be to the left/below the channel offset,
# so adjust if this is the case
self.min_value = min([n.min_value for n in nets])
self.max_value = min([n.max_value for n in nets])
if self.vertical:
real_channel_offset = vector(self.offset.x, min(self.min_value, self.offset.y))
else:
real_channel_offset = vector(min(self.min_value, self.offset.x), self.offset.y)
current_offset = real_channel_offset
# Sort nets by left edge value
nets.sort()
while len(nets) > 0:
current_offset_value = current_offset.y if self.vertical else current_offset.x
# from pprint import pformat # from pprint import pformat
# print("VCG:\n", pformat(vcg)) # print("VCG:\n", pformat(vcg))
# for name,net in vcg.items():
# print(name, net.min_value, net.max_value, net.conflicts)
# print(current_offset)
# get a route from conflict graph with empty fanout set # get a route from conflict graph with empty fanout set
net_name = None for net in nets:
for net_name, conflicts in vcg.items(): # If it has no conflicts and the interval is to the right of the current offset in the track
if len(conflicts) == 0: if net.min_value >= current_offset_value and len(vcg[net.name]) == 0:
vcg = self.remove_net_from_graph(net_name, vcg) # print("Routing {}".format(net.name))
# Add the trunk routes from the bottom up for
# horizontal or the left to right for vertical
if self.vertical:
self.add_vertical_trunk_route(net.pins,
current_offset,
self.vertical_nonpref_pitch)
current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch)
else:
self.add_horizontal_trunk_route(net.pins,
current_offset,
self.horizontal_nonpref_pitch)
current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y)
# Remove the net from other constriants in the VCG
vcg = self.remove_net_from_graph(net.name, vcg)
nets.remove(net)
break break
else: else:
# FIXME: We don't support cyclic VCGs right now. # If we made a full pass and the offset didn't change...
debug.error("Cyclic VCG in channel router.", -1) current_offset_value = current_offset.y if self.vertical else current_offset.x
initial_offset_value = real_channel_offset.y if self.vertical else real_channel_offset.x
# These are the pins we'll have to connect if current_offset_value == initial_offset_value:
pin_list = nets[net_name] debug.info(0, "Channel offset: {}".format(real_channel_offset))
# print("Routing:", net_name, [x.name for x in pin_list]) debug.info(0, "Current offset: {}".format(current_offset))
debug.info(0, "VCG {}".format(str(vcg)))
# Remove the net from other constriants in the VCG debug.info(0, "HCG {}".format(str(hcg)))
vcg = self.remove_net_from_graph(net_name, vcg) for net in nets:
debug.info(0, "{0} pin: {1}".format(net.name, str(net.pins)))
# Add the trunk routes from the bottom up for if self.parent:
# horizontal or the left to right for vertical debug.info(0, "Saving vcg.gds")
if self.vertical: self.parent.gds_write("vcg.gds")
self.add_vertical_trunk_route(pin_list, debug.error("Cyclic VCG in channel router.", -1)
current_offset,
self.vertical_nonpref_pitch)
# This accounts for the via-to-via spacings
current_offset += vector(self.horizontal_nonpref_pitch, 0)
else:
self.add_horizontal_trunk_route(pin_list,
current_offset,
self.horizontal_nonpref_pitch)
# This accounts for the via-to-via spacings
current_offset += vector(0, self.vertical_nonpref_pitch)
# Increment the track and reset the offset to the start (like a typewriter)
if self.vertical:
current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y)
else:
current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch)
# Return the size of the channel # Return the size of the channel
if self.vertical: if self.vertical:
self.width = 0 self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
self.height = current_offset.y self.height = self.max_value + self.vertical_nonpref_pitch - self.offset.y
return current_offset.y + self.vertical_nonpref_pitch - self.offset.y
else: else:
self.width = current_offset.x self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x
self.height = 0 self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y
return current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
def get_layer_pitch(self, layer): def get_layer_pitch(self, layer):
""" Return the track pitch on a given layer """ """ Return the track pitch on a given layer """
try: try:
@ -226,6 +317,17 @@ class channel_route(design.design):
self.add_path(self.vertical_layer, self.add_path(self.vertical_layer,
[vector(min_x - half_layer_width, trunk_offset.y), [vector(min_x - half_layer_width, trunk_offset.y),
vector(max_x + half_layer_width, trunk_offset.y)]) vector(max_x + half_layer_width, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
if pin.cy() < trunk_offset.y:
pin_pos = pin.uc()
else:
pin_pos = pin.bc()
# No bend needed here
mid = vector(pin_pos.x, trunk_offset.y)
self.add_path(self.vertical_layer, [pin_pos, mid])
else: else:
# Add the horizontal trunk # Add the horizontal trunk
self.add_path(self.horizontal_layer, self.add_path(self.horizontal_layer,
@ -269,6 +371,17 @@ class channel_route(design.design):
self.add_path(self.horizontal_layer, self.add_path(self.horizontal_layer,
[vector(trunk_offset.x, min_y - half_layer_width), [vector(trunk_offset.x, min_y - half_layer_width),
vector(trunk_offset.x, max_y + half_layer_width)]) vector(trunk_offset.x, max_y + half_layer_width)])
# Route each pin to the trunk
for pin in pins:
# Find the correct side of the pin
if pin.cx() < trunk_offset.x:
pin_pos = pin.rc()
else:
pin_pos = pin.lc()
# No bend needed here
mid = vector(trunk_offset.x, pin_pos.y)
self.add_path(self.horizontal_layer, [pin_pos, mid])
else: else:
# Add the vertical trunk # Add the vertical trunk
self.add_path(self.vertical_layer, self.add_path(self.vertical_layer,
@ -292,18 +405,4 @@ class channel_route(design.design):
to_layer=self.horizontal_layer, to_layer=self.horizontal_layer,
offset=pin_pos) offset=pin_pos)
def vcg_pin_overlap(self, pin1, pin2, pitch):
""" Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin,
# for example, so the extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
# Pin 1 must be in the "LEFT" set
y_overlap = pin1.lx() < pin2.lx() and abs(pin1.center().y - pin2.center().y) < pitch
overlaps = (not self.vertical and x_overlap) or (self.vertical and y_overlap)
return overlaps

View File

@ -1018,7 +1018,7 @@ class layout():
Wrapper to create a vertical channel route Wrapper to create a vertical channel route
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True) cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=True, parent=self)
self.add_inst("vc", cr) self.add_inst("vc", cr)
self.connect_inst([]) self.connect_inst([])
@ -1027,7 +1027,7 @@ class layout():
Wrapper to create a horizontal channel route Wrapper to create a horizontal channel route
""" """
import channel_route import channel_route
cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False) cr = channel_route.channel_route(netlist, offset, layer_stack, directions, vertical=False, parent=self)
self.add_inst("hc", cr) self.add_inst("hc", cr)
self.connect_inst([]) self.connect_inst([])

View File

@ -1,9 +1,9 @@
word_size = 2 word_size = 2
num_words = 16 num_words = 16
num_rw_ports = 1 num_rw_ports = 0
num_r_ports = 1 num_r_ports = 1
num_w_ports = 0 num_w_ports = 1
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
nominal_corners_only = False nominal_corners_only = False

View File

@ -211,10 +211,11 @@ class bank(design.design):
self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0) self.port_data_offsets[port] = vector(self.main_bitcell_array_left - self.bitcell_array.cell.width, 0)
# UPPER LEFT QUADRANT # UPPER LEFT QUADRANT
# To the left of the bitcell array # To the left of the bitcell array above the predecoders and control logic
x_offset = self.m2_gap + self.port_address.width x_offset = self.m2_gap + self.port_address.width
self.port_address_offsets[port] = vector(-x_offset, self.port_address_offsets[port] = vector(-x_offset,
self.main_bitcell_array_bottom) self.main_bitcell_array_bottom)
self.predecoder_height = self.port_address.predecoder_height + self.port_address_offsets[port].y
# LOWER LEFT QUADRANT # LOWER LEFT QUADRANT
# Place the col decoder left aligned with wordline driver # Place the col decoder left aligned with wordline driver

View File

@ -314,24 +314,23 @@ class hierarchical_decoder(design.design):
for i in range(self.no_of_pre3x8): for i in range(self.no_of_pre3x8):
self.place_pre3x8(i) self.place_pre3x8(i)
self.predecode_height = 0
if self.no_of_pre2x4 > 0:
self.predecode_height = self.pre2x4_inst[-1].uy()
if self.no_of_pre3x8 > 0:
self.predecode_height = self.pre3x8_inst[-1].uy()
def place_pre2x4(self, num): def place_pre2x4(self, num):
""" Place 2x4 predecoder to the left of the origin """ """ Place 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
base = vector(-self.pre2_4.width, 0)
else:
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
self.pre2x4_inst[num].place(base) self.pre2x4_inst[num].place(base)
def place_pre3x8(self, num): def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3): height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) \
offset = vector(-self.pre_3_8.width, 0) + num * (self.pre3_8.height + self.predecoder_spacing)
else: offset = vector(-self.pre3_8.width, height)
height = self.no_of_pre2x4 * (self.pre2_4.height + self.predecoder_spacing) + num * (self.pre3_8.height + self.predecoder_spacing)
offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset) self.pre3x8_inst[num].place(offset)
def create_row_decoder(self): def create_row_decoder(self):

View File

@ -153,6 +153,8 @@ class port_address(design.design):
wordline_driver_offset = vector(self.row_decoder.width, 0) wordline_driver_offset = vector(self.row_decoder.width, 0)
self.wordline_driver_inst.place(wordline_driver_offset) self.wordline_driver_inst.place(wordline_driver_offset)
self.row_decoder_inst.place(row_decoder_offset) self.row_decoder_inst.place(row_decoder_offset)
# Pass this up
self.predecoder_height = self.row_decoder.predecoder_height
self.height = self.row_decoder.height self.height = self.row_decoder.height
self.width = self.wordline_driver_inst.rx() self.width = self.wordline_driver_inst.rx()

View File

@ -10,6 +10,8 @@ from vector import vector
from sram_base import sram_base from sram_base import sram_base
from contact import m2_via from contact import m2_via
from globals import OPTS from globals import OPTS
import channel_route
class sram_1bank(sram_base): class sram_1bank(sram_base):
""" """
@ -58,12 +60,14 @@ class sram_1bank(sram_base):
# the sense amps/column mux and cell array) # the sense amps/column mux and cell array)
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# up to the row address DFFs. # up to the row address DFFs.
control_pos = [None] * len(self.all_ports) self.control_pos = [None] * len(self.all_ports)
row_addr_pos = [None] * len(self.all_ports) self.row_addr_pos = [None] * len(self.all_ports)
col_addr_pos = [None] * len(self.all_ports)
wmask_pos = [None] * len(self.all_ports) # DFFs are placd on their own
spare_wen_pos = [None] * len(self.all_ports) self.col_addr_pos = [None] * len(self.all_ports)
data_pos = [None] * len(self.all_ports) self.wmask_pos = [None] * len(self.all_ports)
self.spare_wen_pos = [None] * len(self.all_ports)
self.data_pos = [None] * len(self.all_ports)
# These positions utilize the channel route sizes. # These positions utilize the channel route sizes.
# FIXME: Auto-compute these rather than manual computation. # FIXME: Auto-compute these rather than manual computation.
@ -75,9 +79,11 @@ class sram_1bank(sram_base):
# Spare wen are on a separate layer so not included # Spare wen are on a separate layer so not included
# Start with 1 track minimum # Start with 1 track minimum
self.data_bus_size = [1] * len(self.all_ports) self.data_bus_size = [1] * len(self.all_ports)
self.col_addr_bus_size = [1] * len(self.all_ports)
for port in self.all_ports: for port in self.all_ports:
# The column address wires are routed separately from the data bus and will always be smaller.
# All ports need the col addr flops # All ports need the col addr flops
self.data_bus_size[port] += self.col_addr_size self.col_addr_bus_size[port] = self.col_addr_size * self.m4_nonpref_pitch
# Write ports need the data input flops and write mask flops # Write ports need the data input flops and write mask flops
if port in self.write_ports: if port in self.write_ports:
self.data_bus_size[port] += self.num_wmasks + self.word_size self.data_bus_size[port] += self.num_wmasks + self.word_size
@ -89,119 +95,146 @@ class sram_1bank(sram_base):
self.data_bus_size[port] *= self.m4_nonpref_pitch self.data_bus_size[port] *= self.m4_nonpref_pitch
# Add the gap in unit length # Add the gap in unit length
self.data_bus_size[port] += self.data_bus_gap self.data_bus_size[port] += self.data_bus_gap
# Port 0
port = 0
# This includes 2 M2 pitches for the row addr clock line. # The control and row addr flops are independent of any bus widths.
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data self.place_control()
# using the control_logic_center value. self.place_row_addr_dffs()
control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y) # Place with an initial wide channel (from above)
self.control_logic_insts[port].place(control_pos[port]) self.place_dffs()
# Route the channel and set to the new data bus size
self.route_dffs(add_routes=False)
# Re-place with the new channel size
self.place_dffs()
# Now route the channel
self.route_dffs()
def place_row_addr_dffs(self):
"""
Must be run after place control logic.
"""
port = 0
# The row address bits are placed above the control logic aligned on the right. # The row address bits are placed above the control logic aligned on the right.
x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width
# It is above the control logic but below the top of the bitcell array # It is above the control logic but below the top of the bitcell array
y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_height)
row_addr_pos[port] = vector(x_offset, y_offset) self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port]) self.row_addr_dff_insts[port].place(self.row_addr_pos[port])
if len(self.all_ports)>1:
port = 1
# The row address bits are placed above the control logic aligned on the left.
x_offset = self.control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# If it can be placed above the predecoder and below the control logic, do it
y_offset = self.bank.bank_array_ll.y
self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(self.row_addr_pos[port], mirror="XY")
def place_control(self):
port = 0
# This includes 2 M2 pitches for the row addr clock line.
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
self.control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch,
self.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(self.control_pos[port])
if len(self.all_ports) > 1:
port = 1
# This includes 2 M2 pitches for the row addr clock line
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
self.control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch,
self.bank.bank_array_ur.y
+ self.control_logic_insts[port].height
- self.control_logic_insts[port].height
+ self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(self.control_pos[port], mirror="XY")
def place_dffs(self):
"""
Place the col addr, data, wmask, and spare data DFFs.
This can be run more than once after we recompute the channel width.
"""
port = 0
# Add the col address flops below the bank to the right of the control logic # Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].rx() + self.dff.width x_offset = self.control_logic_insts[port].rx() + self.dff.width
y_offset = - self.data_bus_size[port] - self.dff.height y_offset = - self.data_bus_size[port] - self.dff.height
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(x_offset, self.col_addr_pos[port] = vector(x_offset,
y_offset) y_offset)
self.col_addr_dff_insts[port].place(col_addr_pos[port]) self.col_addr_dff_insts[port].place(self.col_addr_pos[port])
x_offset = self.col_addr_dff_insts[port].rx() x_offset = self.col_addr_dff_insts[port].rx()
else: else:
col_addr_pos[port] = vector(x_offset, 0) self.col_addr_pos[port] = vector(x_offset, 0)
if port in self.write_ports: if port in self.write_ports:
if self.write_size: if self.write_size:
# Add the write mask flops below the write mask AND array. # Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(x_offset, self.wmask_pos[port] = vector(x_offset,
y_offset) y_offset)
self.wmask_dff_insts[port].place(wmask_pos[port]) self.wmask_dff_insts[port].place(self.wmask_pos[port])
x_offset = self.wmask_dff_insts[port].rx() x_offset = self.wmask_dff_insts[port].rx()
# Add the data flops below the write mask flops. # Add the data flops below the write mask flops.
data_pos[port] = vector(x_offset, self.data_pos[port] = vector(x_offset,
y_offset) y_offset)
self.data_dff_insts[port].place(data_pos[port]) self.data_dff_insts[port].place(self.data_pos[port])
x_offset = self.data_dff_insts[port].rx() x_offset = self.data_dff_insts[port].rx()
# Add spare write enable flops to the right of data flops since the spare columns # Add spare write enable flops to the right of data flops since the spare columns
# will be on the right # will be on the right
if self.num_spare_cols: if self.num_spare_cols:
spare_wen_pos[port] = vector(x_offset, self.spare_wen_pos[port] = vector(x_offset,
y_offset) y_offset)
self.spare_wen_dff_insts[port].place(spare_wen_pos[port]) self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port])
x_offset = self.spare_wen_dff_insts[port].rx() x_offset = self.spare_wen_dff_insts[port].rx()
else: else:
wmask_pos[port] = vector(x_offset, y_offset) self.wmask_pos[port] = vector(x_offset, y_offset)
data_pos[port] = vector(x_offset, y_offset) self.data_pos[port] = vector(x_offset, y_offset)
spare_wen_pos[port] = vector(x_offset, y_offset) self.spare_wen_pos[port] = vector(x_offset, y_offset)
if len(self.all_ports)>1: if len(self.all_ports) > 1:
# Port 1
port = 1 port = 1
# This includes 2 M2 pitches for the row addr clock line
# The delay line is aligned with the bitcell array while the control logic is aligned with the port_data
# using the control_logic_center value.
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2 * self.m2_pitch,
self.bank.bank_array_ur.y
+ self.control_logic_insts[port].height
- self.control_logic_insts[port].height
+ self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(control_pos[port], mirror="XY")
# The row address bits are placed above the control logic aligned on the left.
x_offset = control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# It is below the control logic but below the bottom of the bitcell array
y_offset = min(self.control_logic_insts[port].by(), self.bank_inst.by() + self.row_addr_dff_insts[port].height)
row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port], mirror="XY")
# Add the col address flops below the bank to the right of the control logic # Add the col address flops below the bank to the right of the control logic
x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width x_offset = self.control_logic_insts[port].lx() - 2 * self.dff.width
y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height y_offset = self.bank.height + self.data_bus_size[port] + self.dff.height
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(x_offset - self.col_addr_dff_insts[port].width, self.col_addr_pos[port] = vector(x_offset,
y_offset) y_offset)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") self.col_addr_dff_insts[port].place(self.col_addr_pos[port], mirror="XY")
x_offset = self.col_addr_dff_insts[port].lx() x_offset = self.col_addr_dff_insts[port].lx()
else: else:
col_addr_pos[port] = vector(x_offset, y_offset) self.col_addr_pos[port] = vector(x_offset, y_offset)
if port in self.write_ports: if port in self.write_ports:
# Add spare write enable flops to the right of the data flops since the spare # Add spare write enable flops to the right of the data flops since the spare
# columns will be on the left # columns will be on the left
if self.num_spare_cols: if self.num_spare_cols:
spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width, self.spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width,
y_offset) y_offset)
self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX") self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port], mirror="MX")
x_offset = self.spare_wen_dff_insts[port].lx() x_offset = self.spare_wen_dff_insts[port].lx()
if self.write_size: if self.write_size:
# Add the write mask flops below the write mask AND array. # Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width, self.wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width,
y_offset) y_offset)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") self.wmask_dff_insts[port].place(self.wmask_pos[port], mirror="MX")
x_offset = self.wmask_dff_insts[port].lx() x_offset = self.wmask_dff_insts[port].lx()
# Add the data flops below the write mask flops. # Add the data flops below the write mask flops.
data_pos[port] = vector(x_offset - self.data_dff_insts[port].width, self.data_pos[port] = vector(x_offset - self.data_dff_insts[port].width,
y_offset) y_offset)
self.data_dff_insts[port].place(data_pos[port], mirror="MX") self.data_dff_insts[port].place(self.data_pos[port], mirror="MX")
else: else:
wmask_pos[port] = vector(x_offset, y_offset) self.wmask_pos[port] = vector(x_offset, y_offset)
data_pos[port] = vector(x_offset, y_offset) self.data_pos[port] = vector(x_offset, y_offset)
spare_wen_pos[port] = vector(x_offset, y_offset) self.spare_wen_pos[port] = vector(x_offset, y_offset)
def add_layout_pins(self): def add_layout_pins(self):
""" """
Add the top-level pins for a single bank SRAM with control. Add the top-level pins for a single bank SRAM with control.
@ -343,20 +376,42 @@ class sram_1bank(sram_base):
self.route_row_addr_dff() self.route_row_addr_dff()
def route_dffs(self, add_routes=True):
for port in self.all_ports: for port in self.all_ports:
self.route_dff(port) self.route_dff(port, add_routes)
def route_dff(self, port): def route_dff(self, port, add_routes):
route_map = [] route_map = []
# column mux dff # column mux dff is routed on it's own since it is to the far end
# decoder inputs are min pitch M2, so need to use lower layer stack
if self.col_addr_size > 0: if self.col_addr_size > 0:
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names] dff_pins = [self.col_addr_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)] bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, dff_pins))) route_map.extend(list(zip(bank_pins, dff_pins)))
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
cr = channel_route.channel_route(netlist=route_map,
offset=offset,
layer_stack=self.m1_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.connect_inst([])
else:
self.col_addr_bus_size[port] = cr.height
route_map = []
# wmask dff # wmask dff
if self.num_wmasks > 0 and port in self.write_ports: if self.num_wmasks > 0 and port in self.write_ports:
@ -384,35 +439,46 @@ class sram_1bank(sram_base):
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map.extend(list(zip(bank_pins, sram_pins))) route_map.extend(list(zip(bank_pins, sram_pins)))
if self.num_wmasks > 0 and port in self.write_ports:
layer_stack = self.m3_stack
else:
layer_stack = self.m1_stack
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
if len(route_map) > 0:
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack)
# Route these separately because sometimes the pin pitch on the write driver is too narrow for M3 (FreePDK45)
# spare wen dff # spare wen dff
if self.num_spare_cols > 0 and port in self.write_ports: if self.num_spare_cols > 0 and port in self.write_ports:
dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)] dff_names = ["dout_{}".format(x) for x in range(self.num_spare_cols)]
dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names] dff_pins = [self.spare_wen_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)] bank_names = ["bank_spare_wen{0}_{1}".format(port, x) for x in range(self.num_spare_cols)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map = zip(bank_pins, dff_pins) route_map.extend(list(zip(bank_pins, dff_pins)))
self.create_horizontal_channel_route(netlist=route_map,
if len(route_map) > 0:
if self.num_wmasks > 0 and port in self.write_ports:
layer_stack = self.m3_stack
else:
layer_stack = self.m1_stack
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m1_pitch)
cr = channel_route.channel_route(netlist=route_map,
offset=offset, offset=offset,
layer_stack=self.m1_stack) layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.connect_inst([])
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
else:
offset = vector(0,
self.bank.height + 2 * self.m1_space)
cr = channel_route.channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
if add_routes:
self.add_inst("hc", cr)
self.connect_inst([])
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
def route_clk(self): def route_clk(self):
""" Route the clock network """ """ Route the clock network """