mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' of github.com:VLSIDA/PrivateRAM into dev
This commit is contained in:
commit
317662d4aa
|
|
@ -12,6 +12,69 @@ from vector import vector
|
|||
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):
|
||||
|
||||
unique_id = 0
|
||||
|
|
@ -21,7 +84,8 @@ class channel_route(design.design):
|
|||
offset,
|
||||
layer_stack,
|
||||
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
|
||||
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.directions = directions
|
||||
self.vertical = vertical
|
||||
# For debugging...
|
||||
self.parent = parent
|
||||
|
||||
if not directions or directions == "pref":
|
||||
# Use the preferred layer directions
|
||||
|
|
@ -86,114 +152,139 @@ class channel_route(design.design):
|
|||
# FIXME: This is O(n^2), so maybe optimize it.
|
||||
for other_pin, conflicts in g.items():
|
||||
if pin in conflicts:
|
||||
conflicts.remove(pin)
|
||||
g[other_pin]=conflicts
|
||||
g[other_pin].remove(pin)
|
||||
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):
|
||||
"""
|
||||
Check all the pin pairs on two nets and return a pin
|
||||
overlap if any pin overlaps.
|
||||
"""
|
||||
# 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)
|
||||
# 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:
|
||||
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
|
||||
for net in nets:
|
||||
vcg[net.name] = set()
|
||||
|
||||
return False
|
||||
|
||||
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] = []
|
||||
for net1 in nets:
|
||||
for net2 in nets:
|
||||
# Skip yourself
|
||||
if net_name1 == net_name2:
|
||||
if net1.name == net2.name:
|
||||
continue
|
||||
if self.vcg_nets_overlap(nets[net_name1],
|
||||
nets[net_name2]):
|
||||
vcg[net_name2].append(net_name1)
|
||||
|
||||
if net1.pins_overlap(net2, pitch):
|
||||
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
|
||||
# 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
|
||||
net_name = None
|
||||
for net_name, conflicts in vcg.items():
|
||||
if len(conflicts) == 0:
|
||||
vcg = self.remove_net_from_graph(net_name, vcg)
|
||||
for net in nets:
|
||||
# If it has no conflicts and the interval is to the right of the current offset in the track
|
||||
if net.min_value >= current_offset_value and len(vcg[net.name]) == 0:
|
||||
# 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
|
||||
else:
|
||||
# FIXME: We don't support cyclic VCGs right now.
|
||||
debug.error("Cyclic VCG in channel router.", -1)
|
||||
|
||||
# These are the pins we'll have to connect
|
||||
pin_list = nets[net_name]
|
||||
# print("Routing:", net_name, [x.name for x in pin_list])
|
||||
|
||||
# Remove the net from other constriants in the VCG
|
||||
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)
|
||||
# If we made a full pass and the offset didn't change...
|
||||
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)
|
||||
|
||||
# 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
|
||||
if self.vertical:
|
||||
self.width = 0
|
||||
self.height = current_offset.y
|
||||
return current_offset.y + self.vertical_nonpref_pitch - self.offset.y
|
||||
self.width = current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
|
||||
self.height = self.max_value + self.vertical_nonpref_pitch - self.offset.y
|
||||
else:
|
||||
self.width = current_offset.x
|
||||
self.height = 0
|
||||
return current_offset.x + self.horizontal_nonpref_pitch - self.offset.x
|
||||
|
||||
self.width = self.max_value + self.horizontal_nonpref_pitch - self.offset.x
|
||||
self.height = current_offset.y + self.vertical_nonpref_pitch - self.offset.y
|
||||
|
||||
def get_layer_pitch(self, layer):
|
||||
""" Return the track pitch on a given layer """
|
||||
try:
|
||||
|
|
@ -226,6 +317,17 @@ class channel_route(design.design):
|
|||
self.add_path(self.vertical_layer,
|
||||
[vector(min_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:
|
||||
# Add the horizontal trunk
|
||||
self.add_path(self.horizontal_layer,
|
||||
|
|
@ -269,6 +371,17 @@ class channel_route(design.design):
|
|||
self.add_path(self.horizontal_layer,
|
||||
[vector(trunk_offset.x, min_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:
|
||||
# Add the vertical trunk
|
||||
self.add_path(self.vertical_layer,
|
||||
|
|
@ -292,18 +405,4 @@ class channel_route(design.design):
|
|||
to_layer=self.horizontal_layer,
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -1018,7 +1018,7 @@ class layout():
|
|||
Wrapper to create a vertical 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.connect_inst([])
|
||||
|
||||
|
|
@ -1027,7 +1027,7 @@ class layout():
|
|||
Wrapper to create a horizontal 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.connect_inst([])
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
word_size = 2
|
||||
num_words = 16
|
||||
|
||||
num_rw_ports = 1
|
||||
num_rw_ports = 0
|
||||
num_r_ports = 1
|
||||
num_w_ports = 0
|
||||
num_w_ports = 1
|
||||
|
||||
tech_name = "scn4m_subm"
|
||||
nominal_corners_only = False
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
# 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
|
||||
self.port_address_offsets[port] = vector(-x_offset,
|
||||
self.main_bitcell_array_bottom)
|
||||
self.predecoder_height = self.port_address.predecoder_height + self.port_address_offsets[port].y
|
||||
|
||||
# LOWER LEFT QUADRANT
|
||||
# Place the col decoder left aligned with wordline driver
|
||||
|
|
|
|||
|
|
@ -314,24 +314,23 @@ class hierarchical_decoder(design.design):
|
|||
for i in range(self.no_of_pre3x8):
|
||||
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):
|
||||
""" Place 2x4 predecoder to the left of the origin """
|
||||
|
||||
if (self.num_inputs == 2):
|
||||
base = vector(-self.pre2_4.width, 0)
|
||||
else:
|
||||
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
|
||||
|
||||
base= vector(-self.pre2_4.width, num * (self.pre2_4.height + self.predecoder_spacing))
|
||||
self.pre2x4_inst[num].place(base)
|
||||
|
||||
def place_pre3x8(self, num):
|
||||
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
|
||||
if (self.num_inputs == 3):
|
||||
offset = vector(-self.pre_3_8.width, 0)
|
||||
else:
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
def create_row_decoder(self):
|
||||
|
|
|
|||
|
|
@ -153,6 +153,8 @@ class port_address(design.design):
|
|||
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)
|
||||
# Pass this up
|
||||
self.predecoder_height = self.row_decoder.predecoder_height
|
||||
|
||||
self.height = self.row_decoder.height
|
||||
self.width = self.wordline_driver_inst.rx()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ from vector import vector
|
|||
from sram_base import sram_base
|
||||
from contact import m2_via
|
||||
from globals import OPTS
|
||||
import channel_route
|
||||
|
||||
|
||||
class sram_1bank(sram_base):
|
||||
"""
|
||||
|
|
@ -58,12 +60,14 @@ class sram_1bank(sram_base):
|
|||
# the sense amps/column mux and cell array)
|
||||
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
|
||||
# up to the row address DFFs.
|
||||
control_pos = [None] * len(self.all_ports)
|
||||
row_addr_pos = [None] * len(self.all_ports)
|
||||
col_addr_pos = [None] * len(self.all_ports)
|
||||
wmask_pos = [None] * len(self.all_ports)
|
||||
spare_wen_pos = [None] * len(self.all_ports)
|
||||
data_pos = [None] * len(self.all_ports)
|
||||
self.control_pos = [None] * len(self.all_ports)
|
||||
self.row_addr_pos = [None] * len(self.all_ports)
|
||||
|
||||
# DFFs are placd on their own
|
||||
self.col_addr_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.
|
||||
# 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
|
||||
# Start with 1 track minimum
|
||||
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:
|
||||
# The column address wires are routed separately from the data bus and will always be smaller.
|
||||
# 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
|
||||
if port in self.write_ports:
|
||||
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
|
||||
# Add the gap in unit length
|
||||
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 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.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(control_pos[port])
|
||||
|
||||
# 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_inst.uy() - 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])
|
||||
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
|
||||
|
||||
# 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
|
||||
x_offset = self.control_logic_insts[port].rx() + self.dff.width
|
||||
y_offset = - self.data_bus_size[port] - self.dff.height
|
||||
if self.col_addr_dff:
|
||||
col_addr_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
||||
self.col_addr_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.col_addr_dff_insts[port].place(self.col_addr_pos[port])
|
||||
x_offset = self.col_addr_dff_insts[port].rx()
|
||||
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 self.write_size:
|
||||
# Add the write mask flops below the write mask AND array.
|
||||
wmask_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.wmask_dff_insts[port].place(wmask_pos[port])
|
||||
self.wmask_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.wmask_dff_insts[port].place(self.wmask_pos[port])
|
||||
x_offset = self.wmask_dff_insts[port].rx()
|
||||
|
||||
# Add the data flops below the write mask flops.
|
||||
data_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.data_dff_insts[port].place(data_pos[port])
|
||||
self.data_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.data_dff_insts[port].place(self.data_pos[port])
|
||||
x_offset = self.data_dff_insts[port].rx()
|
||||
|
||||
# Add spare write enable flops to the right of data flops since the spare columns
|
||||
# will be on the right
|
||||
if self.num_spare_cols:
|
||||
spare_wen_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.spare_wen_dff_insts[port].place(spare_wen_pos[port])
|
||||
self.spare_wen_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port])
|
||||
x_offset = self.spare_wen_dff_insts[port].rx()
|
||||
|
||||
else:
|
||||
wmask_pos[port] = vector(x_offset, y_offset)
|
||||
data_pos[port] = vector(x_offset, y_offset)
|
||||
spare_wen_pos[port] = vector(x_offset, y_offset)
|
||||
self.wmask_pos[port] = vector(x_offset, y_offset)
|
||||
self.data_pos[port] = vector(x_offset, y_offset)
|
||||
self.spare_wen_pos[port] = vector(x_offset, y_offset)
|
||||
|
||||
if len(self.all_ports)>1:
|
||||
# Port 1
|
||||
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.
|
||||
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
|
||||
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
|
||||
if self.col_addr_dff:
|
||||
col_addr_pos[port] = vector(x_offset - self.col_addr_dff_insts[port].width,
|
||||
y_offset)
|
||||
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
|
||||
self.col_addr_pos[port] = vector(x_offset,
|
||||
y_offset)
|
||||
self.col_addr_dff_insts[port].place(self.col_addr_pos[port], mirror="XY")
|
||||
x_offset = self.col_addr_dff_insts[port].lx()
|
||||
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:
|
||||
# Add spare write enable flops to the right of the data flops since the spare
|
||||
# columns will be on the left
|
||||
if self.num_spare_cols:
|
||||
spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width,
|
||||
y_offset)
|
||||
self.spare_wen_dff_insts[port].place(spare_wen_pos[port], mirror="MX")
|
||||
self.spare_wen_pos[port] = vector(x_offset - self.spare_wen_dff_insts[port].width,
|
||||
y_offset)
|
||||
self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port], mirror="MX")
|
||||
x_offset = self.spare_wen_dff_insts[port].lx()
|
||||
|
||||
if self.write_size:
|
||||
# Add the write mask flops below the write mask AND array.
|
||||
wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width,
|
||||
y_offset)
|
||||
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
|
||||
self.wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width,
|
||||
y_offset)
|
||||
self.wmask_dff_insts[port].place(self.wmask_pos[port], mirror="MX")
|
||||
x_offset = self.wmask_dff_insts[port].lx()
|
||||
|
||||
# Add the data flops below the write mask flops.
|
||||
data_pos[port] = vector(x_offset - self.data_dff_insts[port].width,
|
||||
y_offset)
|
||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||
self.data_pos[port] = vector(x_offset - self.data_dff_insts[port].width,
|
||||
y_offset)
|
||||
self.data_dff_insts[port].place(self.data_pos[port], mirror="MX")
|
||||
else:
|
||||
wmask_pos[port] = vector(x_offset, y_offset)
|
||||
data_pos[port] = vector(x_offset, y_offset)
|
||||
spare_wen_pos[port] = vector(x_offset, y_offset)
|
||||
|
||||
self.wmask_pos[port] = vector(x_offset, y_offset)
|
||||
self.data_pos[port] = vector(x_offset, y_offset)
|
||||
self.spare_wen_pos[port] = vector(x_offset, y_offset)
|
||||
|
||||
def add_layout_pins(self):
|
||||
"""
|
||||
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()
|
||||
|
||||
def route_dffs(self, add_routes=True):
|
||||
|
||||
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 = []
|
||||
|
||||
# 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:
|
||||
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]
|
||||
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]
|
||||
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
|
||||
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]
|
||||
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
|
||||
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_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_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||
route_map = zip(bank_pins, dff_pins)
|
||||
self.create_horizontal_channel_route(netlist=route_map,
|
||||
route_map.extend(list(zip(bank_pins, dff_pins)))
|
||||
|
||||
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,
|
||||
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):
|
||||
""" Route the clock network """
|
||||
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue