port_data: Refactor channel_route/connect_bitlines()

both functions share a lot of code and are passing around a lot of data
under similar names (inst1, inst1_start_bit, inst1_bl_name, ...). Thus
we group all these elements in a named tuple to ease passing around
these elements.

All callers of channel_route/connect_bitlines() either pass in the bl/br
names or rely on "br_{}"/"bl_{}" as defaults. These hard coded values
should be determined by the instances. Thus we get the bitline names
based on the instances passed in. The callers only provide a template
string, to take care of the case that bitlines are called "bl_out_{}".

Signed-off-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de>
This commit is contained in:
Bastian Koppelmann 2020-02-14 14:00:43 +01:00
parent 5e1f64c8f9
commit 656fdd1008
1 changed files with 106 additions and 53 deletions

View File

@ -8,6 +8,7 @@ from tech import drc, parameter
import debug
import design
from sram_factory import factory
from collections import namedtuple
from vector import vector
from globals import OPTS
@ -535,13 +536,16 @@ class port_data(design.design):
# Only do this if we have a column mux!
if self.col_addr_size==0:
return
inst1 = self.column_mux_array_inst
inst2 = self.precharge_array_inst
if self.port==0:
self.connect_bitlines(inst1, inst2, self.num_cols, inst2_start_bit=1)
else:
self.connect_bitlines(inst1, inst2, self.num_cols)
insn2_start_bit = 1 if self.port == 0 else 0
self.connect_bitlines(inst1=inst1,
inst2=inst2,
num_bits=self.num_cols,
inst2_start_bit=insn2_start_bit)
def route_sense_amp_to_column_mux_or_precharge_array(self, port):
@ -551,46 +555,49 @@ class port_data(design.design):
if self.col_addr_size>0:
# Sense amp is connected to the col mux
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
inst1_bls_templ = "{inst}_out_{bit}"
start_bit = 0
else:
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
inst1_bls_templ="{inst}_{bit}"
if self.port==0:
start_bit=1
else:
start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=inst1,
inst1_bls_template=inst1_bls_templ,
inst2=inst2,
num_bits=self.word_size,
inst1_start_bit=start_bit)
def route_write_driver_to_column_mux_or_precharge_array(self, port):
""" Routing of BL and BR between sense_amp and column mux or precharge array """
inst2 = self.write_driver_array_inst
inst2_bl_name = inst2.mod.get_bl_name()
inst2_br_name = inst2.mod.get_br_name()
if self.col_addr_size>0:
# Write driver is connected to the col mux
inst1 = self.column_mux_array_inst
inst1_bl_name = "bl_out_{}"
inst1_br_name = "br_out_{}"
inst1_bls_templ = "{inst}_out_{bit}"
start_bit = 0
else:
# Sense amp is directly connected to the precharge array
inst1 = self.precharge_array_inst
inst1_bl_name = "bl_{}"
inst1_br_name = "br_{}"
inst1_bls_templ="{inst}_{bit}"
if self.port==0:
start_bit=1
else:
start_bit=0
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name, inst1_start_bit=start_bit)
self.channel_route_bitlines(inst1=inst1, inst2=inst2,
num_bits=self.word_size,
inst1_bls_template=inst1_bls_templ,
inst1_start_bit=start_bit)
def route_write_driver_to_sense_amp(self, port):
@ -601,7 +608,9 @@ class port_data(design.design):
# These should be pitch matched in the cell library,
# but just in case, do a channel route.
self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size)
self.channel_route_bitlines(inst1=inst1,
inst2=inst2,
num_bits=self.word_size)
def route_bitline_pins(self):
@ -647,64 +656,108 @@ class port_data(design.design):
if self.write_mask_and_array_inst:
self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en")
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
def _group_bitline_instances(self, inst1, inst2, num_bits,
inst1_bls_template,
inst1_start_bit,
inst2_bls_template,
inst2_start_bit):
"""
Route the bl and br of two modules using the channel router.
Groups all the parameters into a named tuple and seperates them into
top and bottom instances.
"""
inst_group = namedtuple('InstanceGroup', ('inst', 'bls_template',
'bl_name', 'br_name', 'start_bit'))
inst1_group = inst_group(inst1, inst1_bls_template,
inst1.mod.get_bl_name(),
inst1.mod.get_br_name(),
inst1_start_bit)
inst2_group = inst_group(inst2, inst2_bls_template,
inst2.mod.get_bl_name(),
inst2.mod.get_br_name(),
inst2_start_bit)
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
bot_inst_group = inst1_group
top_inst_group = inst2_group
else:
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
bot_inst_group = inst2_group
top_inst_group = inst1_group
return (bot_inst_group, top_inst_group)
def _get_bitline_pins(self, inst_group, bit):
"""
Extracts bl/br pins from an InstanceGroup based on the bit modifier.
"""
full_bl_name = inst_group.bls_template.format(
**{'inst' : inst_group.bl_name,
'bit' : inst_group.start_bit + bit}
)
full_br_name = inst_group.bls_template.format(
**{'inst' : inst_group.br_name,
'bit' : inst_group.start_bit + bit}
)
return (inst_group.inst.get_pin(full_bl_name),
inst_group.inst.get_pin(full_br_name))
def channel_route_bitlines(self, inst1, inst2, num_bits,
inst1_bls_template="{inst}_{bit}",
inst1_start_bit=0,
inst2_bls_template="{inst}_{bit}",
inst2_start_bit=0):
"""
Route the bl and br of two modules using the channel router.
"""
bot_inst_group, top_inst_group = self._group_bitline_instances(
inst1, inst2, num_bits,
inst1_bls_template, inst1_start_bit,
inst2_bls_template, inst2_start_bit)
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
offset = bot_inst_group.inst.ul() + vector(0,self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit+bottom_start_bit)), bottom_inst.get_pin(bottom_br_name.format(bit+bottom_start_bit))]
top_names = [top_inst.get_pin(top_bl_name.format(bit+top_start_bit)), top_inst.get_pin(top_br_name.format(bit+top_start_bit))]
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
top_names = self._get_bitline_pins(top_inst_group, bit)
route_map = list(zip(bottom_names, top_names))
self.create_horizontal_channel_route(route_map, offset, self.m1_stack)
def connect_bitlines(self, inst1, inst2, num_bits,
inst1_bl_name="bl_{}", inst1_br_name="br_{}", inst1_start_bit=0,
inst2_bl_name="bl_{}", inst2_br_name="br_{}", inst2_start_bit=0):
inst1_bls_template="{inst}_{bit}",
inst1_start_bit=0,
inst2_bls_template="{inst}_{bit}",
inst2_start_bit=0):
"""
Connect the bl and br of two modules.
This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed).
"""
# determine top and bottom automatically.
# since they don't overlap, we can just check the bottom y coordinate.
if inst1.by() < inst2.by():
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
else:
(bottom_inst, bottom_bl_name, bottom_br_name, bottom_start_bit) = (inst2, inst2_bl_name, inst2_br_name, inst2_start_bit)
(top_inst, top_bl_name, top_br_name, top_start_bit) = (inst1, inst1_bl_name, inst1_br_name, inst1_start_bit)
bot_inst_group, top_inst_group = self._group_bitline_instances(
inst1, inst2, num_bits,
inst1_bls_template, inst1_start_bit,
inst2_bls_template, inst2_start_bit)
bottom_inst = bot_inst_group.inst
top_inst = top_inst_group.inst
for col in range(num_bits):
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col+bottom_start_bit)).uc()
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col+bottom_start_bit)).uc()
top_bl = top_inst.get_pin(top_bl_name.format(col+top_start_bit)).bc()
top_br = top_inst.get_pin(top_br_name.format(col+top_start_bit)).bc()
bot_bl_pin, bot_br_pin = self._get_bitline_pins(bot_inst_group, col)
top_bl_pin, top_br_pin = self._get_bitline_pins(top_inst_group, col)
bot_bl, bot_br = bot_bl_pin.uc(), bot_br_pin.uc()
top_bl, top_br = top_bl_pin.bc(), top_br_pin.bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y)
self.add_path("m2",[bottom_bl, vector(bottom_bl.x,yoffset),
yoffset = 0.5*(top_bl.y+bot_bl.y)
self.add_path("m2",[bot_bl, vector(bot_bl.x,yoffset),
vector(top_bl.x,yoffset), top_bl])
self.add_path("m2",[bottom_br, vector(bottom_br.x,yoffset),
self.add_path("m2",[bot_br, vector(bot_br.x,yoffset),
vector(top_br.x,yoffset), top_br])
def graph_exclude_precharge(self):