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,113 +152,138 @@ 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 vcg_nets_overlap(self, net1, net2):
"""
Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps.
"""
if self.vertical:
pitch = self.horizontal_nonpref_pitch
else:
pitch = self.vertical_nonpref_pitch
for pin1 in net1:
for pin2 in net2:
if self.vcg_pin_overlap(pin1, pin2, pitch):
return True
return False
def route(self): def route(self):
# FIXME: Must extend this to a horizontal conflict graph # Create names for the nets for the graphs
# too if we want to minimize the nets = []
# number of tracks! index = 0
# hcg = {} # print(self.netlist)
for pin_list in self.netlist:
nets.append(channel_net("n{}".format(index), pin_list, self.vertical))
index += 1
# Create the (undirected) horizontal constraint graph
hcg = collections.OrderedDict()
for net1 in nets:
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) # Initialize the vertical conflict graph (vcg)
# and make a list of all pins # and make a list of all pins
vcg = collections.OrderedDict() 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:") # print("Nets:")
# for net_name in nets: # for net_name in nets:
# print(net_name, [x.name for x in nets[net_name]]) # print(net_name, [x.name for x in nets[net_name]])
# Find the vertical pin conflicts # Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now # FIXME: O(n^2) but who cares for now
for net_name1 in nets: if self.vertical:
if net_name1 not in vcg.keys(): pitch = self.horizontal_nonpref_pitch
vcg[net_name1] = [] else:
for net_name2 in nets: pitch = self.vertical_nonpref_pitch
if net_name2 not in vcg.keys():
vcg[net_name2] = [] for net in nets:
vcg[net.name] = set()
for net1 in nets:
for net2 in nets:
# 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]):
vcg[net_name2].append(net_name1)
current_offset = self.offset if net1.pins_overlap(net2, pitch):
vcg[net2.name].add(net1.name)
# Check if there are any cycles net1 <---> net2 in the 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
# list of routes to do
while vcg:
# 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
if current_offset_value == initial_offset_value:
debug.info(0, "Channel offset: {}".format(real_channel_offset))
debug.info(0, "Current offset: {}".format(current_offset))
debug.info(0, "VCG {}".format(str(vcg)))
debug.info(0, "HCG {}".format(str(hcg)))
for net in nets:
debug.info(0, "{0} pin: {1}".format(net.name, str(net.pins)))
if self.parent:
debug.info(0, "Saving vcg.gds")
self.parent.gds_write("vcg.gds")
debug.error("Cyclic VCG in channel router.", -1)
# These are the pins we'll have to connect # Increment the track and reset the offset to the start (like a typewriter)
pin_list = nets[net_name] if self.vertical:
# print("Routing:", net_name, [x.name for x in pin_list]) current_offset = vector(current_offset.x + self.horizontal_nonpref_pitch, real_channel_offset.y)
else:
# Remove the net from other constriants in the VCG current_offset = vector(real_channel_offset.x, current_offset.y + self.vertical_nonpref_pitch)
vcg = self.remove_net_from_graph(net_name, vcg)
# 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(pin_list,
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)
# 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 """
@ -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
@ -90,117 +96,144 @@ class sram_1bank(sram_base):
# 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 # The control and row addr flops are independent of any bus widths.
self.place_control()
self.place_row_addr_dffs()
# Place with an initial wide channel (from above)
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.
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
y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_height)
self.row_addr_pos[port] = vector(x_offset, y_offset)
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 port = 0
# This includes 2 M2 pitches for the row addr clock line. # 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 # 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. # using the control_logic_center value.
control_pos[port] = vector(-self.control_logic_insts[port].width - 2 * self.m2_pitch, 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.bank.bank_array_ll.y - self.control_logic_insts[port].mod.control_logic_center.y)
self.control_logic_insts[port].place(control_pos[port]) 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")
# The row address bits are placed above the control logic aligned on the right. def place_dffs(self):
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 Place the col addr, data, wmask, and spare data DFFs.
y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) This can be run more than once after we recompute the channel width.
row_addr_pos[port] = vector(x_offset, y_offset) """
self.row_addr_dff_insts[port].place(row_addr_pos[port])
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):
""" """
@ -343,14 +376,17 @@ class sram_1bank(sram_base):
self.route_row_addr_dff() self.route_row_addr_dff()
for port in self.all_ports: def route_dffs(self, add_routes=True):
self.route_dff(port)
def route_dff(self, port): for port in self.all_ports:
self.route_dff(port, add_routes)
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]
@ -358,6 +394,25 @@ 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, 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:
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)] dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
@ -384,34 +439,45 @@ 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 """