mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into pdriver
This commit is contained in:
commit
8ea85e3e65
14
.coveragerc
14
.coveragerc
|
|
@ -4,6 +4,10 @@ omit =
|
||||||
*/.local/*
|
*/.local/*
|
||||||
# omit everything in /usr
|
# omit everything in /usr
|
||||||
/usr/*
|
/usr/*
|
||||||
|
# ignore the unit tests themselves
|
||||||
|
*/tests/*
|
||||||
|
# ignore the debug utilities
|
||||||
|
debug.py
|
||||||
[paths]
|
[paths]
|
||||||
source =
|
source =
|
||||||
/home/gitlab-runner/builds/2fd64746/0
|
/home/gitlab-runner/builds/2fd64746/0
|
||||||
|
|
@ -12,3 +16,13 @@ source =
|
||||||
/home/gitlab-runner/builds/2fd64746/3
|
/home/gitlab-runner/builds/2fd64746/3
|
||||||
/home/gitlab-runner/builds/2fd64746/4
|
/home/gitlab-runner/builds/2fd64746/4
|
||||||
/home/gitlab-runner/builds/2fd64746/5
|
/home/gitlab-runner/builds/2fd64746/5
|
||||||
|
[report]
|
||||||
|
exclude_lines =
|
||||||
|
pragma: no cover
|
||||||
|
def __repr__
|
||||||
|
except Exception
|
||||||
|
raise AssertionError
|
||||||
|
raise NotImplementedError
|
||||||
|
if 0:
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if not OPTS.is_unit_test
|
||||||
|
|
@ -1,93 +1,45 @@
|
||||||
|
TECH = scn4m_subm
|
||||||
CUR_DIR = $(shell pwd)
|
CUR_DIR = $(shell pwd)
|
||||||
TEST_DIR = ${CUR_DIR}/tests
|
TEST_DIR = ${CUR_DIR}/tests
|
||||||
|
|
||||||
MAKEFLAGS += -j 2
|
MAKEFLAGS += -j 1
|
||||||
|
|
||||||
# Library test
|
# Library test
|
||||||
LIBRARY_TESTS = \
|
LIBRARY_TESTS = $(shell find ${TEST_DIR} -name 0[1-2]*_test.py)
|
||||||
01_library_drc_test.py \
|
|
||||||
02_library_lvs_test.py
|
|
||||||
|
|
||||||
# Technology and DRC tests (along with ptx)
|
# Technology and DRC tests (along with ptx)
|
||||||
TECH_TESTS = \
|
TECH_TESTS = $(shell find ${TEST_DIR} -name 03*_test.py)
|
||||||
03_contact_test.py \
|
|
||||||
03_ptx_1finger_pmos_test.py \
|
|
||||||
03_ptx_4finger_nmos_test.py \
|
|
||||||
03_path_test.py \
|
|
||||||
03_ptx_3finger_nmos_test.py \
|
|
||||||
03_ptx_4finger_pmos_test.py \
|
|
||||||
03_ptx_1finger_nmos_test.py \
|
|
||||||
03_ptx_3finger_pmos_test.py \
|
|
||||||
03_wire_test.py
|
|
||||||
|
|
||||||
# Parameterized cells
|
# Parameterized cells
|
||||||
PCELLS_TESTS = \
|
CELL_TESTS = $(shell find ${TEST_DIR} -name 04*_test.py)
|
||||||
04_pinv_1x_test.py \
|
|
||||||
04_pinv_1x_beta_test.py \
|
|
||||||
04_pinv_2x_test.py \
|
|
||||||
04_pinv_10x_test.py \
|
|
||||||
04_pnand2_test.py \
|
|
||||||
04_pnor2_test.py \
|
|
||||||
04_pnand3_test.py\
|
|
||||||
04_wordline_driver_test.py \
|
|
||||||
04_precharge_test.py
|
|
||||||
|
|
||||||
# Dynamically generated modules and arrays
|
# Dynamically generated modules and arrays
|
||||||
MODULE_TESTS = \
|
MODULE_TESTS = $(shell find ${TEST_DIR} -name 0[5-9]*_test.py)\
|
||||||
05_bitcell_array_test.py \
|
$(shell find ${TEST_DIR} -name 1*_test.py)
|
||||||
06_hierarchical_decoder_test.py \
|
|
||||||
06_hierarchical_predecode2x4_test.py \
|
|
||||||
06_hierarchical_predecode3x8_test.py \
|
|
||||||
07_single_level_column_mux_array_test.py \
|
|
||||||
08_precharge_array_test.py \
|
|
||||||
09_sense_amp_array_test.py \
|
|
||||||
10_write_driver_array_test.py \
|
|
||||||
11_ms_flop_array_test.py \
|
|
||||||
12_tri_gate_array_test.py \
|
|
||||||
13_delay_chain_test.py \
|
|
||||||
14_replica_bitline_test.py \
|
|
||||||
16_control_logic_test.py
|
|
||||||
|
|
||||||
# Top-level SRAM configurations
|
# Top-level SRAM configurations
|
||||||
TOP_TESTS = \
|
TOP_TESTS = $(shell find ${TEST_DIR} -name 20*_test.py)
|
||||||
19_multi_bank_test.py \
|
|
||||||
19_single_bank_test.py \
|
|
||||||
20_sram_1bank_test.py \
|
|
||||||
20_sram_2bank_test.py \
|
|
||||||
20_sram_4bank_test.py
|
|
||||||
|
|
||||||
# All simulation tests.
|
# All simulation tests.
|
||||||
CHAR_TESTS = \
|
CHAR_TESTS = $(shell find ${TEST_DIR} -name 2[1-2]*_test.py)
|
||||||
21_hspice_delay_test.py \
|
|
||||||
21_ngspice_delay_test.py \
|
|
||||||
21_ngspice_setuphold_test.py \
|
|
||||||
21_hspice_setuphold_test.py \
|
|
||||||
22_sram_func_test.py \
|
|
||||||
22_pex_func_test_with_pinv.py \
|
|
||||||
23_lib_sram_prune_test.py \
|
|
||||||
23_lib_sram_test.py
|
|
||||||
|
|
||||||
# Keep the model lib test here since it is fast
|
# Keep the model lib test here since it is fast
|
||||||
# and doesn't need simulation.
|
# and doesn't need simulation.
|
||||||
USAGE_TESTS = \
|
USAGE_TESTS = $(shell find ${TEST_DIR} -name 2[3-9]*_test.py)\
|
||||||
23_lib_sram_model_test.py \
|
$(shell find ${TEST_DIR} -name 30*_test.py)
|
||||||
24_lef_sram_test.py \
|
|
||||||
25_verilog_sram_test.py
|
|
||||||
|
|
||||||
ALL_FILES = \
|
ALL_TESTS = \
|
||||||
${LIBRARY_TESTS} \
|
${LIBRARY_TESTS} \
|
||||||
${TECH_TESTS} \
|
${TECH_TESTS} \
|
||||||
${PCELLS_TESTS} \
|
${CELL_TESTS} \
|
||||||
${MODULES_TESTS} \
|
${MODULE_TESTS} \
|
||||||
${TOP_TESTS} \
|
${TOP_TESTS} \
|
||||||
${CHAR_TESTS} \
|
${CHAR_TESTS} \
|
||||||
${USAGE_TESTS}
|
${USAGE_TESTS}
|
||||||
|
|
||||||
default all:
|
.PHONY: ${ALL_TESTS}
|
||||||
|
|
||||||
$(ALL_FILES):
|
all: ${ALL_TESTS}
|
||||||
python ${TEST_DIR}/$@ -t freepdk45
|
|
||||||
python ${TEST_DIR}/$@ -t scn3me_subm
|
|
||||||
|
|
||||||
# Library tests
|
# Library tests
|
||||||
lib: ${LIBRARY_TESTS}
|
lib: ${LIBRARY_TESTS}
|
||||||
|
|
@ -96,10 +48,10 @@ lib: ${LIBRARY_TESTS}
|
||||||
tech: ${TECH_TESTS}
|
tech: ${TECH_TESTS}
|
||||||
|
|
||||||
# Dynamically generated cells
|
# Dynamically generated cells
|
||||||
pcells: ${PCELLS_TESTS}
|
cell: ${CELL_TESTS}
|
||||||
|
|
||||||
# Dynamically generated modules
|
# Dynamically generated modules
|
||||||
modules: ${MODULES_TESTS}
|
module: ${MODULE_TESTS}
|
||||||
|
|
||||||
# Top level SRAM tests
|
# Top level SRAM tests
|
||||||
top: ${TOP_TESTS}
|
top: ${TOP_TESTS}
|
||||||
|
|
@ -110,6 +62,9 @@ char: ${CHAR_TESTS}
|
||||||
# Usage and file generation
|
# Usage and file generation
|
||||||
usage: ${USAGE_TESTS}
|
usage: ${USAGE_TESTS}
|
||||||
|
|
||||||
|
$(ALL_TESTS):
|
||||||
|
python3 $@ -t ${TECH}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
find . -name \*.pyc -exec rm {} \;
|
find . -name \*.pyc -exec rm {} \;
|
||||||
find . -name \*~ -exec rm {} \;
|
find . -name \*~ -exec rm {} \;
|
||||||
|
|
|
||||||
|
|
@ -673,11 +673,13 @@ class layout(lef.lef):
|
||||||
offset=bus_pos,
|
offset=bus_pos,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
def add_horizontal_trunk_route(self, pins, trunk_offset,
|
def add_horizontal_trunk_route(self,
|
||||||
|
pins,
|
||||||
|
trunk_offset,
|
||||||
layer_stack=("metal1", "via1", "metal2"),
|
layer_stack=("metal1", "via1", "metal2"),
|
||||||
pitch=None):
|
pitch=None):
|
||||||
"""
|
"""
|
||||||
Create a trunk route for all pins with the the trunk located at the given y offset.
|
Create a trunk route for all pins with the trunk located at the given y offset.
|
||||||
"""
|
"""
|
||||||
if not pitch:
|
if not pitch:
|
||||||
pitch = self.m1_pitch
|
pitch = self.m1_pitch
|
||||||
|
|
@ -704,15 +706,18 @@ class layout(lef.lef):
|
||||||
|
|
||||||
# Route each pin to the trunk
|
# Route each pin to the trunk
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
# Bend to the center of the trunk so it adds a via automatically
|
|
||||||
mid = vector(pin.center().x, trunk_offset.y)
|
mid = vector(pin.center().x, trunk_offset.y)
|
||||||
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
|
self.add_path(layer_stack[2], [pin.center(), mid])
|
||||||
|
self.add_via_center(layers=layer_stack,
|
||||||
|
offset=mid)
|
||||||
|
|
||||||
def add_vertical_trunk_route(self, pins, trunk_offset,
|
def add_vertical_trunk_route(self,
|
||||||
|
pins,
|
||||||
|
trunk_offset,
|
||||||
layer_stack=("metal1", "via1", "metal2"),
|
layer_stack=("metal1", "via1", "metal2"),
|
||||||
pitch=None):
|
pitch=None):
|
||||||
"""
|
"""
|
||||||
Create a trunk route for all pins with the the trunk located at the given x offset.
|
Create a trunk route for all pins with the trunk located at the given x offset.
|
||||||
"""
|
"""
|
||||||
if not pitch:
|
if not pitch:
|
||||||
pitch = self.m2_pitch
|
pitch = self.m2_pitch
|
||||||
|
|
@ -740,18 +745,21 @@ class layout(lef.lef):
|
||||||
|
|
||||||
# Route each pin to the trunk
|
# Route each pin to the trunk
|
||||||
for pin in pins:
|
for pin in pins:
|
||||||
# Bend to the center of the trunk so it adds a via automatically
|
|
||||||
mid = vector(trunk_offset.x, pin.center().y)
|
mid = vector(trunk_offset.x, pin.center().y)
|
||||||
self.add_wire(layer_stack, [pin.center(), mid, trunk_mid])
|
self.add_path(layer_stack[0], [pin.center(), mid])
|
||||||
|
self.add_via_center(layers=layer_stack,
|
||||||
|
offset=mid,
|
||||||
|
rotate=90)
|
||||||
|
|
||||||
|
|
||||||
def create_channel_route(self, netlist, pins, offset,
|
def create_channel_route(self, netlist,
|
||||||
layer_stack=("metal1", "via1", "metal2"), pitch=None,
|
offset,
|
||||||
|
layer_stack=("metal1", "via1", "metal2"),
|
||||||
|
pitch=None,
|
||||||
vertical=False):
|
vertical=False):
|
||||||
"""
|
"""
|
||||||
The net list is a list of the nets. Each net is a list of pin
|
The net list is a list of the nets. Each net is a list of pins
|
||||||
names to be connected. Pins is a dictionary of the pin names
|
to be connected. Offset is the lower-left of where the
|
||||||
to the pin structures. Offset is the lower-left of where the
|
|
||||||
routing channel will start. This does NOT try to minimize the
|
routing channel will start. This does NOT try to minimize the
|
||||||
number of tracks -- instead, it picks an order to avoid the
|
number of tracks -- instead, it picks an order to avoid the
|
||||||
vertical conflicts between pins.
|
vertical conflicts between pins.
|
||||||
|
|
@ -786,7 +794,10 @@ class layout(lef.lef):
|
||||||
|
|
||||||
def vcg_pin_overlap(pin1, pin2, vertical):
|
def vcg_pin_overlap(pin1, pin2, vertical):
|
||||||
""" Check for vertical or horizontal overlap of the two pins """
|
""" 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
|
# Pin 1 must be in the "BOTTOM" set
|
||||||
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
|
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
|
||||||
|
|
||||||
|
|
@ -815,10 +826,7 @@ class layout(lef.lef):
|
||||||
for pin_list in netlist:
|
for pin_list in netlist:
|
||||||
net_name = "n{}".format(index)
|
net_name = "n{}".format(index)
|
||||||
index += 1
|
index += 1
|
||||||
nets[net_name] = []
|
nets[net_name] = pin_list
|
||||||
for pin_name in pin_list:
|
|
||||||
pin = pins[pin_name]
|
|
||||||
nets[net_name].append(pin)
|
|
||||||
|
|
||||||
# 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
|
||||||
|
|
@ -834,8 +842,6 @@ class layout(lef.lef):
|
||||||
if vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical):
|
if vcg_nets_overlap(nets[net_name1], nets[net_name2], vertical):
|
||||||
vcg[net_name2].append(net_name1)
|
vcg[net_name2].append(net_name1)
|
||||||
|
|
||||||
#FIXME: What if we have a cycle?
|
|
||||||
|
|
||||||
# list of routes to do
|
# list of routes to do
|
||||||
while vcg:
|
while vcg:
|
||||||
#from pprint import pformat
|
#from pprint import pformat
|
||||||
|
|
@ -868,23 +874,21 @@ class layout(lef.lef):
|
||||||
offset += vector(0,pitch)
|
offset += vector(0,pitch)
|
||||||
|
|
||||||
|
|
||||||
def create_vertical_channel_route(self, netlist, pins, offset,
|
def create_vertical_channel_route(self, netlist, offset,
|
||||||
layer_stack=("metal1", "via1", "metal2"),
|
layer_stack=("metal1", "via1", "metal2"),
|
||||||
pitch=None):
|
pitch=None):
|
||||||
"""
|
"""
|
||||||
Wrapper to create a vertical channel route
|
Wrapper to create a vertical channel route
|
||||||
"""
|
"""
|
||||||
self.create_channel_route(netlist, pins, offset, layer_stack,
|
self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=True)
|
||||||
pitch, vertical=True)
|
|
||||||
|
|
||||||
def create_horizontal_channel_route(self, netlist, pins, offset,
|
def create_horizontal_channel_route(self, netlist, offset,
|
||||||
layer_stack=("metal1", "via1", "metal2"),
|
layer_stack=("metal1", "via1", "metal2"),
|
||||||
pitch=None):
|
pitch=None):
|
||||||
"""
|
"""
|
||||||
Wrapper to create a horizontal channel route
|
Wrapper to create a horizontal channel route
|
||||||
"""
|
"""
|
||||||
self.create_channel_route(netlist, pins, offset,
|
self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=False)
|
||||||
layer_stack, pitch, vertical=False)
|
|
||||||
|
|
||||||
def add_enclosure(self, insts, layer="nwell"):
|
def add_enclosure(self, insts, layer="nwell"):
|
||||||
""" Add a layer that surrounds the given instances. Useful
|
""" Add a layer that surrounds the given instances. Useful
|
||||||
|
|
@ -922,21 +926,30 @@ class layout(lef.lef):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_power_pin(self, name, loc, rotate=90):
|
def add_power_pin(self, name, loc, rotate=90, start_layer="metal1"):
|
||||||
"""
|
"""
|
||||||
Add a single power pin from M3 down to M1 at the given center location
|
Add a single power pin from M3 down to M1 at the given center location.
|
||||||
|
The starting layer is specified to determine which vias are needed.
|
||||||
"""
|
"""
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
||||||
offset=loc,
|
if start_layer=="metal1":
|
||||||
rotate=float(rotate))
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
|
|
||||||
offset=loc,
|
offset=loc,
|
||||||
rotate=float(rotate))
|
rotate=float(rotate))
|
||||||
self.add_layout_pin_rect_center(text=name,
|
if start_layer=="metal1" or start_layer=="metal2":
|
||||||
layer="metal3",
|
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
|
||||||
offset=loc,
|
offset=loc,
|
||||||
width=via.width,
|
rotate=float(rotate))
|
||||||
height=via.height)
|
if start_layer=="metal3":
|
||||||
|
self.add_layout_pin_rect_center(text=name,
|
||||||
|
layer="metal3",
|
||||||
|
offset=loc)
|
||||||
|
else:
|
||||||
|
self.add_layout_pin_rect_center(text=name,
|
||||||
|
layer="metal3",
|
||||||
|
offset=loc,
|
||||||
|
width=via.width,
|
||||||
|
height=via.height)
|
||||||
|
|
||||||
def add_power_ring(self, bbox):
|
def add_power_ring(self, bbox):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -441,10 +441,10 @@ class pin_layout:
|
||||||
"""
|
"""
|
||||||
Given three co-linear points, determine if q lies on segment pr
|
Given three co-linear points, determine if q lies on segment pr
|
||||||
"""
|
"""
|
||||||
if q[0] <= max(p[0], r[0]) and \
|
if q.x <= max(p.x, r.x) and \
|
||||||
q[0] >= min(p[0], r[0]) and \
|
q.x >= min(p.x, r.x) and \
|
||||||
q[1] <= max(p[1], r[1]) and \
|
q.y <= max(p.y, r.y) and \
|
||||||
q[1] >= min(p[1], r[1]):
|
q.y >= min(p.y, r.y):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
@ -473,8 +473,8 @@ class pin_layout:
|
||||||
x = (b2*c1 - b1*c2)/determinant
|
x = (b2*c1 - b1*c2)/determinant
|
||||||
y = (a1*c2 - a2*c1)/determinant
|
y = (a1*c2 - a2*c1)/determinant
|
||||||
|
|
||||||
r = [x,y]
|
r = vector(x,y).snap_to_grid()
|
||||||
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
|
if self.on_segment(a, r, b) and self.on_segment(c, r, d):
|
||||||
return [x, y]
|
return r
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ class pbitcell(design.design):
|
||||||
self.offset_all_coordinates()
|
self.offset_all_coordinates()
|
||||||
gnd_overlap = vector(0, 0.5*contact.well.width)
|
gnd_overlap = vector(0, 0.5*contact.well.width)
|
||||||
self.translate_all(gnd_overlap)
|
self.translate_all(gnd_overlap)
|
||||||
self.DRC_LVS()
|
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
""" add pins and set names for bitlines and wordlines """
|
""" add pins and set names for bitlines and wordlines """
|
||||||
|
|
@ -323,20 +323,21 @@ class pbitcell(design.design):
|
||||||
# Add rails for vdd and gnd
|
# Add rails for vdd and gnd
|
||||||
gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing
|
gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing
|
||||||
self.gnd_position = vector(0, gnd_ypos)
|
self.gnd_position = vector(0, gnd_ypos)
|
||||||
self.gnd = self.add_layout_pin_rect_center(text="gnd",
|
self.add_rect_center(layer="metal1",
|
||||||
layer="metal1",
|
offset=self.gnd_position,
|
||||||
offset=self.gnd_position,
|
width=self.width,
|
||||||
width=self.width,
|
height=self.m1_width)
|
||||||
height=self.m1_width)
|
self.add_power_pin("gnd", vector(0,gnd_ypos))
|
||||||
|
|
||||||
|
|
||||||
vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset
|
vdd_ypos = self.inverter_nmos_ypos + self.inverter_nmos.active_height + self.inverter_gap + self.inverter_pmos.active_height + self.vdd_offset
|
||||||
self.vdd_position = vector(0, vdd_ypos)
|
self.vdd_position = vector(0, vdd_ypos)
|
||||||
self.vdd = self.add_layout_pin_rect_center(text="vdd",
|
self.add_rect_center(layer="metal1",
|
||||||
layer="metal1",
|
offset=self.vdd_position,
|
||||||
offset=self.vdd_position,
|
width=self.width,
|
||||||
width=self.width,
|
height=self.m1_width)
|
||||||
height=self.m1_width)
|
self.add_power_pin("vdd", vector(0,vdd_ypos))
|
||||||
|
|
||||||
def create_readwrite_ports(self):
|
def create_readwrite_ports(self):
|
||||||
"""
|
"""
|
||||||
Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell.
|
Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell.
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ class functional(simulation):
|
||||||
self.set_spice_constants()
|
self.set_spice_constants()
|
||||||
self.set_stimulus_variables()
|
self.set_stimulus_variables()
|
||||||
self.create_signal_names()
|
self.create_signal_names()
|
||||||
|
|
||||||
|
|
||||||
# Number of checks can be changed
|
# Number of checks can be changed
|
||||||
self.num_cycles = 2
|
self.num_cycles = 2
|
||||||
|
|
@ -141,17 +142,17 @@ class functional(simulation):
|
||||||
sp_read_value = ""
|
sp_read_value = ""
|
||||||
for bit in range(self.word_size):
|
for bit in range(self.word_size):
|
||||||
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check))
|
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check))
|
||||||
if value > 0.88 * self.vdd_voltage:
|
if value > self.v_high:
|
||||||
sp_read_value = "1" + sp_read_value
|
sp_read_value = "1" + sp_read_value
|
||||||
elif value < 0.12 * self.vdd_voltage:
|
elif value < self.v_low:
|
||||||
sp_read_value = "0" + sp_read_value
|
sp_read_value = "0" + sp_read_value
|
||||||
else:
|
else:
|
||||||
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
|
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
|
||||||
bit,
|
bit,
|
||||||
value,
|
value,
|
||||||
eo_period,
|
eo_period,
|
||||||
0.12*self.vdd_voltage,
|
self.v_low,
|
||||||
0.88*self.vdd_voltage)
|
self.v_high)
|
||||||
return (0, error)
|
return (0, error)
|
||||||
|
|
||||||
self.read_check.append([sp_read_value, dout_port, eo_period, check])
|
self.read_check.append([sp_read_value, dout_port, eo_period, check])
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,9 @@ class simulation():
|
||||||
self.period = tech.spice["feasible_period"]
|
self.period = tech.spice["feasible_period"]
|
||||||
self.slew = tech.spice["rise_time"]*2
|
self.slew = tech.spice["rise_time"]*2
|
||||||
self.load = tech.spice["msflop_in_cap"]*4
|
self.load = tech.spice["msflop_in_cap"]*4
|
||||||
|
|
||||||
|
self.v_high = self.vdd_voltage - tech.spice["v_threshold_typical"]
|
||||||
|
self.v_low = tech.spice["v_threshold_typical"]
|
||||||
self.gnd_voltage = 0
|
self.gnd_voltage = 0
|
||||||
|
|
||||||
def set_stimulus_variables(self):
|
def set_stimulus_variables(self):
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ num_words = 16
|
||||||
|
|
||||||
tech_name = "scn4m_subm"
|
tech_name = "scn4m_subm"
|
||||||
process_corners = ["TT"]
|
process_corners = ["TT"]
|
||||||
supply_voltages = [ 3.3 ]
|
supply_voltages = [5.0]
|
||||||
temperatures = [ 25 ]
|
temperatures = [25]
|
||||||
|
|
||||||
output_path = "temp"
|
output_path = "temp"
|
||||||
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
|
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ def parse_args():
|
||||||
# This may be overridden when we read a config file though...
|
# This may be overridden when we read a config file though...
|
||||||
if OPTS.tech_name == "":
|
if OPTS.tech_name == "":
|
||||||
OPTS.tech_name = "scmos"
|
OPTS.tech_name = "scmos"
|
||||||
# Alias SCMOS to AMI 0.5um
|
# Alias SCMOS to 180nm
|
||||||
if OPTS.tech_name == "scmos":
|
if OPTS.tech_name == "scmos":
|
||||||
OPTS.tech_name = "scn4m_subm"
|
OPTS.tech_name = "scn4m_subm"
|
||||||
|
|
||||||
|
|
@ -89,6 +89,7 @@ def print_banner():
|
||||||
print("|=========" + dev_info.center(60) + "=========|")
|
print("|=========" + dev_info.center(60) + "=========|")
|
||||||
temp_info = "Temp dir: {}".format(OPTS.openram_temp)
|
temp_info = "Temp dir: {}".format(OPTS.openram_temp)
|
||||||
print("|=========" + temp_info.center(60) + "=========|")
|
print("|=========" + temp_info.center(60) + "=========|")
|
||||||
|
print("|=========" + "See LICENSE for license info".center(60) + "=========|")
|
||||||
print("|==============================================================================|")
|
print("|==============================================================================|")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -412,6 +413,9 @@ def report_status():
|
||||||
print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
|
print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
|
||||||
OPTS.num_words,
|
OPTS.num_words,
|
||||||
OPTS.num_banks))
|
OPTS.num_banks))
|
||||||
|
print("RW ports: {0}\nR-only ports: {1}\nW-only ports: {2}".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports))
|
||||||
if OPTS.netlist_only:
|
if OPTS.netlist_only:
|
||||||
print("Netlist only mode (no physical design is being done).")
|
print("Netlist only mode (no physical design is being done).")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ from pinv import pinv
|
||||||
from pnand2 import pnand2
|
from pnand2 import pnand2
|
||||||
from pnor2 import pnor2
|
from pnor2 import pnor2
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from pinvbuf import pinvbuf
|
|
||||||
|
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
||||||
|
|
@ -85,11 +84,12 @@ class bank(design.design):
|
||||||
self.add_pin("bank_sel{}".format(port),"INPUT")
|
self.add_pin("bank_sel{}".format(port),"INPUT")
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
self.add_pin("s_en{0}".format(port), "INPUT")
|
self.add_pin("s_en{0}".format(port), "INPUT")
|
||||||
|
for port in self.read_ports:
|
||||||
|
self.add_pin("p_en_bar{0}".format(port), "INPUT")
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
self.add_pin("w_en{0}".format(port), "INPUT")
|
self.add_pin("w_en{0}".format(port), "INPUT")
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.add_pin("clk_buf_bar{0}".format(port),"INPUT")
|
self.add_pin("wl_en{0}".format(port), "INPUT")
|
||||||
self.add_pin("clk_buf{0}".format(port),"INPUT")
|
|
||||||
self.add_pin("vdd","POWER")
|
self.add_pin("vdd","POWER")
|
||||||
self.add_pin("gnd","GROUND")
|
self.add_pin("gnd","GROUND")
|
||||||
|
|
||||||
|
|
@ -226,16 +226,19 @@ class bank(design.design):
|
||||||
# UPPER LEFT QUADRANT
|
# UPPER LEFT QUADRANT
|
||||||
# To the left of the bitcell array
|
# To the left of the bitcell array
|
||||||
# The wordline driver is placed to the right of the main decoder width.
|
# The wordline driver is placed to the right of the main decoder width.
|
||||||
x_offset = self.central_bus_width + self.wordline_driver.width
|
x_offset = self.m2_gap + self.wordline_driver.width
|
||||||
self.wordline_driver_offsets[port] = vector(-x_offset,0)
|
self.wordline_driver_offsets[port] = vector(-x_offset,0)
|
||||||
x_offset += self.row_decoder.width + self.m2_gap
|
x_offset += self.row_decoder.width + self.m2_gap
|
||||||
self.row_decoder_offsets[port] = vector(-x_offset,0)
|
self.row_decoder_offsets[port] = vector(-x_offset,0)
|
||||||
|
|
||||||
# LOWER LEFT QUADRANT
|
# LOWER LEFT QUADRANT
|
||||||
|
# Place the col decoder left aligned with wordline driver plus halfway under row decoder
|
||||||
# Place the col decoder left aligned with row decoder (x_offset doesn't change)
|
# Place the col decoder left aligned with row decoder (x_offset doesn't change)
|
||||||
# Below the bitcell array with well spacing
|
# Below the bitcell array with well spacing
|
||||||
|
x_offset = self.central_bus_width[port] + self.wordline_driver.width
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
y_offset = self.column_decoder.height
|
x_offset += self.column_decoder.width + self.col_addr_bus_width
|
||||||
|
y_offset = self.m2_gap + self.column_decoder.height
|
||||||
else:
|
else:
|
||||||
y_offset = 0
|
y_offset = 0
|
||||||
y_offset += 2*drc("well_to_well")
|
y_offset += 2*drc("well_to_well")
|
||||||
|
|
@ -283,16 +286,18 @@ class bank(design.design):
|
||||||
# LOWER RIGHT QUADRANT
|
# LOWER RIGHT QUADRANT
|
||||||
# To the left of the bitcell array
|
# To the left of the bitcell array
|
||||||
# The wordline driver is placed to the right of the main decoder width.
|
# The wordline driver is placed to the right of the main decoder width.
|
||||||
x_offset = self.bitcell_array.width + self.central_bus_width + self.wordline_driver.width
|
x_offset = self.bitcell_array.width + self.m2_gap + self.wordline_driver.width
|
||||||
self.wordline_driver_offsets[port] = vector(x_offset,0)
|
self.wordline_driver_offsets[port] = vector(x_offset,0)
|
||||||
x_offset += self.row_decoder.width + self.m2_gap
|
x_offset += self.row_decoder.width + self.m2_gap
|
||||||
self.row_decoder_offsets[port] = vector(x_offset,0)
|
self.row_decoder_offsets[port] = vector(x_offset,0)
|
||||||
|
|
||||||
# UPPER RIGHT QUADRANT
|
# UPPER RIGHT QUADRANT
|
||||||
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
|
# Place the col decoder right aligned with wordline driver plus halfway under row decoder
|
||||||
# Above the bitcell array with a well spacing
|
# Above the bitcell array with a well spacing
|
||||||
|
x_offset = self.bitcell_array.width + self.central_bus_width[port] + self.wordline_driver.width
|
||||||
if self.col_addr_size > 0:
|
if self.col_addr_size > 0:
|
||||||
y_offset = self.bitcell_array.height + self.column_decoder.height
|
x_offset += self.column_decoder.width + self.col_addr_bus_width
|
||||||
|
y_offset = self.bitcell_array.height + self.column_decoder.height + self.m2_gap
|
||||||
else:
|
else:
|
||||||
y_offset = self.bitcell_array.height
|
y_offset = self.bitcell_array.height
|
||||||
y_offset += 2*drc("well_to_well")
|
y_offset += 2*drc("well_to_well")
|
||||||
|
|
@ -349,21 +354,25 @@ class bank(design.design):
|
||||||
# FIXME: This spacing should be width dependent...
|
# FIXME: This spacing should be width dependent...
|
||||||
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
|
self.supply_rail_pitch = self.supply_rail_width + 4*self.m2_space
|
||||||
|
|
||||||
# Number of control lines in the bus
|
|
||||||
self.num_control_lines = 4
|
|
||||||
# The order of the control signals on the control bus:
|
# The order of the control signals on the control bus:
|
||||||
self.input_control_signals = []
|
self.input_control_signals = []
|
||||||
port_num = 0
|
port_num = 0
|
||||||
for port in range(OPTS.num_rw_ports):
|
for port in range(OPTS.num_rw_ports):
|
||||||
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num)])
|
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
|
||||||
port_num += 1
|
port_num += 1
|
||||||
for port in range(OPTS.num_w_ports):
|
for port in range(OPTS.num_w_ports):
|
||||||
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "w_en{}".format(port_num)])
|
self.input_control_signals.append(["wl_en{}".format(port_num), "w_en{}".format(port_num)])
|
||||||
port_num += 1
|
port_num += 1
|
||||||
for port in range(OPTS.num_r_ports):
|
for port in range(OPTS.num_r_ports):
|
||||||
self.input_control_signals.append(["clk_buf{}".format(port_num), "clk_buf_bar{}".format(port_num), "s_en{}".format(port_num)])
|
self.input_control_signals.append(["wl_en{}".format(port_num), "s_en{}".format(port_num), "p_en_bar{}".format(port_num)])
|
||||||
port_num += 1
|
port_num += 1
|
||||||
|
|
||||||
|
# Number of control lines in the bus for each port
|
||||||
|
self.num_control_lines = [len(x) for x in self.input_control_signals]
|
||||||
|
|
||||||
|
# The width of this bus is needed to place other modules (e.g. decoder) for each port
|
||||||
|
self.central_bus_width = [self.m2_pitch*x + self.m2_width for x in self.num_control_lines]
|
||||||
|
|
||||||
# These will be outputs of the gaters if this is multibank, if not, normal signals.
|
# These will be outputs of the gaters if this is multibank, if not, normal signals.
|
||||||
self.control_signals = []
|
self.control_signals = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
|
|
@ -371,15 +380,13 @@ class bank(design.design):
|
||||||
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
|
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
|
||||||
else:
|
else:
|
||||||
self.control_signals.append(self.input_control_signals[port])
|
self.control_signals.append(self.input_control_signals[port])
|
||||||
|
|
||||||
# The central bus is the column address (one hot) and row address (binary)
|
# The central bus is the column address (one hot) and row address (binary)
|
||||||
if self.col_addr_size>0:
|
if self.col_addr_size>0:
|
||||||
self.num_col_addr_lines = 2**self.col_addr_size
|
self.num_col_addr_lines = 2**self.col_addr_size
|
||||||
else:
|
else:
|
||||||
self.num_col_addr_lines = 0
|
self.num_col_addr_lines = 0
|
||||||
|
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
|
||||||
# The width of this bus is needed to place other modules (e.g. decoder)
|
|
||||||
# A width on each side too
|
|
||||||
self.central_bus_width = self.m2_pitch * self.num_control_lines + self.m2_width
|
|
||||||
|
|
||||||
# A space for wells or jogging m2
|
# A space for wells or jogging m2
|
||||||
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
|
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
|
||||||
|
|
@ -401,16 +408,15 @@ class bank(design.design):
|
||||||
setattr (self, "mod_"+mod_name, mod_class)
|
setattr (self, "mod_"+mod_name, mod_class)
|
||||||
|
|
||||||
|
|
||||||
self.bitcell = self.mod_bitcell()
|
|
||||||
|
|
||||||
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
|
||||||
rows=self.num_rows)
|
rows=self.num_rows)
|
||||||
self.add_mod(self.bitcell_array)
|
self.add_mod(self.bitcell_array)
|
||||||
|
|
||||||
# create arrays of bitline and bitline_bar names for read, write, or all ports
|
# create arrays of bitline and bitline_bar names for read, write, or all ports
|
||||||
|
self.bitcell = self.mod_bitcell()
|
||||||
self.bl_names = self.bitcell.list_all_bl_names()
|
self.bl_names = self.bitcell.list_all_bl_names()
|
||||||
self.br_names = self.bitcell.list_all_br_names()
|
self.br_names = self.bitcell.list_all_br_names()
|
||||||
|
|
||||||
self.wl_names = self.bitcell.list_all_wl_names()
|
self.wl_names = self.bitcell.list_all_wl_names()
|
||||||
self.bitline_names = self.bitcell.list_all_bitline_names()
|
self.bitline_names = self.bitcell.list_all_bitline_names()
|
||||||
|
|
||||||
|
|
@ -489,7 +495,7 @@ class bank(design.design):
|
||||||
for i in range(self.num_cols):
|
for i in range(self.num_cols):
|
||||||
temp.append(self.bl_names[port]+"_{0}".format(i))
|
temp.append(self.bl_names[port]+"_{0}".format(i))
|
||||||
temp.append(self.br_names[port]+"_{0}".format(i))
|
temp.append(self.br_names[port]+"_{0}".format(i))
|
||||||
temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"])
|
temp.extend([self.prefix+"p_en_bar{0}".format(port), "vdd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -664,7 +670,7 @@ class bank(design.design):
|
||||||
temp.append("dec_out{0}_{1}".format(port,row))
|
temp.append("dec_out{0}_{1}".format(port,row))
|
||||||
for row in range(self.num_rows):
|
for row in range(self.num_rows):
|
||||||
temp.append(self.wl_names[port]+"_{0}".format(row))
|
temp.append(self.wl_names[port]+"_{0}".format(row))
|
||||||
temp.append(self.prefix+"clk_buf{0}".format(port))
|
temp.append(self.prefix+"wl_en{0}".format(port))
|
||||||
temp.append("vdd")
|
temp.append("vdd")
|
||||||
temp.append("gnd")
|
temp.append("gnd")
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
@ -691,16 +697,19 @@ class bank(design.design):
|
||||||
if self.col_addr_size == 0:
|
if self.col_addr_size == 0:
|
||||||
return
|
return
|
||||||
elif self.col_addr_size == 1:
|
elif self.col_addr_size == 1:
|
||||||
|
from pinvbuf import pinvbuf
|
||||||
self.column_decoder = pinvbuf(height=self.mod_dff.height)
|
self.column_decoder = pinvbuf(height=self.mod_dff.height)
|
||||||
self.add_mod(self.column_decoder)
|
|
||||||
elif self.col_addr_size == 2:
|
elif self.col_addr_size == 2:
|
||||||
self.column_decoder = self.row_decoder.pre2_4
|
from hierarchical_predecode2x4 import hierarchical_predecode2x4 as pre2x4
|
||||||
|
self.column_decoder = pre2x4(height=self.mod_dff.height)
|
||||||
elif self.col_addr_size == 3:
|
elif self.col_addr_size == 3:
|
||||||
self.column_decoder = self.row_decoder.pre3_8
|
from hierarchical_predecode3x8 import hierarchical_predecode3x8 as pre3x8
|
||||||
|
self.column_decoder = pre3x8(height=self.mod_dff.height)
|
||||||
else:
|
else:
|
||||||
# No error checking before?
|
# No error checking before?
|
||||||
debug.error("Invalid column decoder?",-1)
|
debug.error("Invalid column decoder?",-1)
|
||||||
|
self.add_mod(self.column_decoder)
|
||||||
|
|
||||||
self.column_decoder_inst = [None]*len(self.all_ports)
|
self.column_decoder_inst = [None]*len(self.all_ports)
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
|
||||||
|
|
@ -774,14 +783,14 @@ class bank(design.design):
|
||||||
""" Route the bank select logic. """
|
""" Route the bank select logic. """
|
||||||
|
|
||||||
if self.port_id[port] == "rw":
|
if self.port_id[port] == "rw":
|
||||||
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
|
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
|
||||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
|
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
|
||||||
elif self.port_id[port] == "w":
|
elif self.port_id[port] == "w":
|
||||||
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"]
|
bank_sel_signals = ["clk_buf", "w_en", "bank_sel"]
|
||||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"]
|
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"]
|
||||||
else:
|
else:
|
||||||
bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"]
|
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
|
||||||
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"]
|
gated_bank_sel_signals = ["gated_clk_buf", "gated_s_en", "gated_p_en_bar"]
|
||||||
|
|
||||||
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
|
copy_control_signals = self.input_control_signals[port]+["bank_sel{}".format(port)]
|
||||||
for signal in range(len(copy_control_signals)):
|
for signal in range(len(copy_control_signals)):
|
||||||
|
|
@ -835,8 +844,9 @@ class bank(design.design):
|
||||||
# Port 0
|
# Port 0
|
||||||
# The bank is at (0,0), so this is to the left of the y-axis.
|
# The bank is at (0,0), so this is to the left of the y-axis.
|
||||||
# 2 pitches on the right for vias/jogs to access the inputs
|
# 2 pitches on the right for vias/jogs to access the inputs
|
||||||
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
|
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, self.min_y_offset)
|
||||||
control_bus_length = self.max_y_offset - self.min_y_offset
|
# The control bus is routed up to two pitches below the bitcell array
|
||||||
|
control_bus_length = -2*self.m1_pitch - self.min_y_offset
|
||||||
self.bus_xoffset[0] = self.create_bus(layer="metal2",
|
self.bus_xoffset[0] = self.create_bus(layer="metal2",
|
||||||
pitch=self.m2_pitch,
|
pitch=self.m2_pitch,
|
||||||
offset=control_bus_offset,
|
offset=control_bus_offset,
|
||||||
|
|
@ -847,7 +857,11 @@ class bank(design.design):
|
||||||
|
|
||||||
# Port 1
|
# Port 1
|
||||||
if len(self.all_ports)==2:
|
if len(self.all_ports)==2:
|
||||||
control_bus_offset = vector(self.bitcell_array.width + self.m2_width, self.min_y_offset)
|
# The other control bus is routed up to two pitches above the bitcell array
|
||||||
|
control_bus_length = self.max_y_offset - self.bitcell_array.height - 2*self.m1_pitch
|
||||||
|
control_bus_offset = vector(self.bitcell_array.width + self.m2_width,
|
||||||
|
self.max_y_offset - control_bus_length)
|
||||||
|
|
||||||
self.bus_xoffset[1] = self.create_bus(layer="metal2",
|
self.bus_xoffset[1] = self.create_bus(layer="metal2",
|
||||||
pitch=self.m2_pitch,
|
pitch=self.m2_pitch,
|
||||||
offset=control_bus_offset,
|
offset=control_bus_offset,
|
||||||
|
|
@ -891,6 +905,9 @@ class bank(design.design):
|
||||||
inst1 = self.bitcell_array_inst
|
inst1 = self.bitcell_array_inst
|
||||||
inst1_bl_name = self.bl_names[port]+"_{}"
|
inst1_bl_name = self.bl_names[port]+"_{}"
|
||||||
inst1_br_name = self.br_names[port]+"_{}"
|
inst1_br_name = self.br_names[port]+"_{}"
|
||||||
|
|
||||||
|
# The column mux is constructed to match the bitline pitch, so we can directly connect
|
||||||
|
# here and not channel route the bitlines.
|
||||||
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
|
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.num_cols,
|
||||||
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
|
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
|
||||||
|
|
||||||
|
|
@ -911,8 +928,8 @@ class bank(design.design):
|
||||||
inst1_bl_name = "bl_{}"
|
inst1_bl_name = "bl_{}"
|
||||||
inst1_br_name = "br_{}"
|
inst1_br_name = "br_{}"
|
||||||
|
|
||||||
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
|
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_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
|
||||||
|
|
||||||
def route_write_driver_to_column_mux_or_bitcell_array(self, port):
|
def route_write_driver_to_column_mux_or_bitcell_array(self, port):
|
||||||
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
|
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
|
||||||
|
|
@ -937,7 +954,11 @@ class bank(design.design):
|
||||||
|
|
||||||
inst1 = self.write_driver_array_inst[port]
|
inst1 = self.write_driver_array_inst[port]
|
||||||
inst2 = self.sense_amp_array_inst[port]
|
inst2 = self.sense_amp_array_inst[port]
|
||||||
self.connect_bitlines(inst1, inst2, self.word_size)
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def route_sense_amp_out(self, port):
|
def route_sense_amp_out(self, port):
|
||||||
|
|
@ -992,14 +1013,10 @@ class bank(design.design):
|
||||||
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
|
# 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 = bottom_inst.ul() + vector(0,self.m1_pitch)
|
||||||
for bit in range(num_bits):
|
for bit in range(num_bits):
|
||||||
bottom_names = [bottom_bl_name.format(bit), bottom_br_name.format(bit)]
|
bottom_names = [bottom_inst.get_pin(bottom_bl_name.format(bit)), bottom_inst.get_pin(bottom_br_name.format(bit))]
|
||||||
top_names = [top_bl_name.format(bit), top_br_name.format(bit)]
|
top_names = [top_inst.get_pin(top_bl_name.format(bit)), top_inst.get_pin(top_br_name.format(bit))]
|
||||||
route_map = list(zip(bottom_names, top_names))
|
route_map = list(zip(bottom_names, top_names))
|
||||||
bottom_pins = {key: bottom_inst.get_pin(key) for key in bottom_names }
|
self.create_horizontal_channel_route(route_map, offset)
|
||||||
top_pins = {key: top_inst.get_pin(key) for key in top_names }
|
|
||||||
all_pins = {**bottom_pins, **top_pins}
|
|
||||||
debug.check(len(all_pins)==len(bottom_pins)+len(top_pins),"Duplicate named pins in bitline channel route.")
|
|
||||||
self.create_horizontal_channel_route(route_map, all_pins, offset)
|
|
||||||
|
|
||||||
|
|
||||||
def connect_bitlines(self, inst1, inst2, num_bits,
|
def connect_bitlines(self, inst1, inst2, num_bits,
|
||||||
|
|
@ -1078,79 +1095,41 @@ class bank(design.design):
|
||||||
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
|
||||||
|
|
||||||
def route_column_address_lines(self, port):
|
def route_column_address_lines(self, port):
|
||||||
|
""" Connecting the select lines of column mux to the address bus """
|
||||||
|
if not self.col_addr_size>0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.col_addr_size == 1:
|
||||||
|
|
||||||
|
# Connect to sel[0] and sel[1]
|
||||||
|
decode_names = ["Zb", "Z"]
|
||||||
|
|
||||||
|
# The Address LSB
|
||||||
|
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
||||||
|
|
||||||
|
elif self.col_addr_size > 1:
|
||||||
|
decode_names = []
|
||||||
|
for i in range(self.num_col_addr_lines):
|
||||||
|
decode_names.append("out_{}".format(i))
|
||||||
|
|
||||||
|
for i in range(self.col_addr_size):
|
||||||
|
decoder_name = "in_{}".format(i)
|
||||||
|
addr_name = "addr{0}_{1}".format(port,i)
|
||||||
|
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
||||||
|
|
||||||
if port%2:
|
if port%2:
|
||||||
self.route_column_address_lines_right(port)
|
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
|
||||||
else:
|
else:
|
||||||
self.route_column_address_lines_left(port)
|
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
||||||
|
|
||||||
def route_column_address_lines_left(self, port):
|
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
|
||||||
""" Connecting the select lines of column mux to the address bus """
|
|
||||||
if not self.col_addr_size>0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.col_addr_size == 1:
|
|
||||||
|
|
||||||
# Connect to sel[0] and sel[1]
|
|
||||||
decode_names = ["Zb", "Z"]
|
|
||||||
|
|
||||||
# The Address LSB
|
|
||||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
|
||||||
|
|
||||||
elif self.col_addr_size > 1:
|
|
||||||
decode_names = []
|
|
||||||
for i in range(self.num_col_addr_lines):
|
|
||||||
decode_names.append("out_{}".format(i))
|
|
||||||
|
|
||||||
for i in range(self.col_addr_size):
|
|
||||||
decoder_name = "in_{}".format(i)
|
|
||||||
addr_name = "addr{0}_{1}".format(port,i)
|
|
||||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
|
||||||
|
|
||||||
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
|
||||||
|
|
||||||
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
|
||||||
|
|
||||||
route_map = list(zip(decode_names, sel_names))
|
|
||||||
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
|
|
||||||
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
|
|
||||||
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
|
||||||
all_pins = {**decode_pins, **column_mux_pins}
|
|
||||||
self.create_vertical_channel_route(route_map, all_pins, offset)
|
|
||||||
|
|
||||||
def route_column_address_lines_right(self, port):
|
|
||||||
""" Connecting the select lines of column mux to the address bus """
|
|
||||||
if not self.col_addr_size>0:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.col_addr_size == 1:
|
|
||||||
|
|
||||||
# Connect to sel[0] and sel[1]
|
|
||||||
decode_names = ["Zb", "Z"]
|
|
||||||
|
|
||||||
# The Address LSB
|
|
||||||
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
|
|
||||||
|
|
||||||
elif self.col_addr_size > 1:
|
|
||||||
decode_names = []
|
|
||||||
for i in range(self.num_col_addr_lines):
|
|
||||||
decode_names.append("out_{}".format(i))
|
|
||||||
|
|
||||||
for i in range(self.col_addr_size):
|
|
||||||
decoder_name = "in_{}".format(i)
|
|
||||||
addr_name = "addr{0}_{1}".format(port,i)
|
|
||||||
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
|
|
||||||
|
|
||||||
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
|
|
||||||
|
|
||||||
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
|
||||||
|
|
||||||
route_map = list(zip(decode_names, sel_names))
|
|
||||||
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
|
|
||||||
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
|
|
||||||
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
|
||||||
all_pins = {**decode_pins, **column_mux_pins}
|
|
||||||
self.create_vertical_channel_route(route_map, all_pins, offset)
|
|
||||||
|
|
||||||
|
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
||||||
|
column_mux_pins = [self.column_mux_array_inst[port].get_pin(x) for x in sel_names]
|
||||||
|
|
||||||
|
route_map = list(zip(decode_pins, column_mux_pins))
|
||||||
|
self.create_vertical_channel_route(route_map, offset)
|
||||||
|
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
def add_lvs_correspondence_points(self):
|
||||||
""" This adds some points for easier debugging if LVS goes wrong.
|
""" This adds some points for easier debugging if LVS goes wrong.
|
||||||
|
|
@ -1209,7 +1188,7 @@ class bank(design.design):
|
||||||
|
|
||||||
connection = []
|
connection = []
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc()))
|
connection.append((self.prefix+"p_en_bar{}".format(port), self.precharge_array_inst[port].get_pin("en_bar").lc()))
|
||||||
|
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc()))
|
connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc()))
|
||||||
|
|
@ -1225,9 +1204,13 @@ class bank(design.design):
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
# clk to wordline_driver
|
# clk to wordline_driver
|
||||||
control_signal = self.prefix+"clk_buf{}".format(port)
|
control_signal = self.prefix+"wl_en{}".format(port)
|
||||||
pin_pos = self.wordline_driver_inst[port].get_pin("en").bc()
|
if port%2:
|
||||||
mid_pos = pin_pos - vector(0,self.m1_pitch)
|
pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").uc()
|
||||||
|
mid_pos = pin_pos + vector(0,self.m2_gap) # to route down to the top of the bus
|
||||||
|
else:
|
||||||
|
pin_pos = self.wordline_driver_inst[port].get_pin("en_bar").bc()
|
||||||
|
mid_pos = pin_pos - vector(0,self.m2_gap) # to route down to the top of the bus
|
||||||
control_x_offset = self.bus_xoffset[port][control_signal].x
|
control_x_offset = self.bus_xoffset[port][control_signal].x
|
||||||
control_pos = vector(control_x_offset, mid_pos.y)
|
control_pos = vector(control_x_offset, mid_pos.y)
|
||||||
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
|
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])
|
||||||
|
|
|
||||||
|
|
@ -132,40 +132,14 @@ class bitcell_array(design.design):
|
||||||
# increments to the next row height
|
# increments to the next row height
|
||||||
offset.y += self.cell.height
|
offset.y += self.cell.height
|
||||||
|
|
||||||
# For every second row and column, add a via for vdd
|
# For every second row and column, add a via for gnd and vdd
|
||||||
for row in range(self.row_size):
|
for row in range(self.row_size):
|
||||||
for col in range(self.column_size):
|
for col in range(self.column_size):
|
||||||
inst = self.cell_inst[row,col]
|
inst = self.cell_inst[row,col]
|
||||||
for vdd_pin in inst.get_pins("vdd"):
|
for pin_name in ["vdd", "gnd"]:
|
||||||
# Drop to M1 if needed
|
for pin in inst.get_pins(pin_name):
|
||||||
if vdd_pin.layer == "metal1":
|
self.add_power_pin(pin_name, pin.center(), 0, pin.layer)
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
||||||
offset=vdd_pin.center(),
|
|
||||||
rotate=90)
|
|
||||||
# Always drop to M2
|
|
||||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
|
||||||
offset=vdd_pin.center())
|
|
||||||
self.add_layout_pin_rect_center(text="vdd",
|
|
||||||
layer="metal3",
|
|
||||||
offset=vdd_pin.center())
|
|
||||||
|
|
||||||
|
|
||||||
# For every second row and column (+1), add a via for gnd
|
|
||||||
for row in range(self.row_size):
|
|
||||||
for col in range(self.column_size):
|
|
||||||
inst = self.cell_inst[row,col]
|
|
||||||
for gnd_pin in inst.get_pins("gnd"):
|
|
||||||
# Drop to M1 if needed
|
|
||||||
if gnd_pin.layer == "metal1":
|
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
|
||||||
offset=gnd_pin.center(),
|
|
||||||
rotate=90)
|
|
||||||
# Always drop to M2
|
|
||||||
self.add_via_center(layers=("metal2", "via2", "metal3"),
|
|
||||||
offset=gnd_pin.center())
|
|
||||||
self.add_layout_pin_rect_center(text="gnd",
|
|
||||||
layer="metal3",
|
|
||||||
offset=gnd_pin.center())
|
|
||||||
|
|
||||||
def analytical_delay(self, slew, load=0):
|
def analytical_delay(self, slew, load=0):
|
||||||
from tech import drc
|
from tech import drc
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ from tech import drc, parameter
|
||||||
import debug
|
import debug
|
||||||
import contact
|
import contact
|
||||||
from pinv import pinv
|
from pinv import pinv
|
||||||
|
from pbuf import pbuf
|
||||||
|
from pand2 import pand2
|
||||||
from pnand2 import pnand2
|
from pnand2 import pnand2
|
||||||
from pnand3 import pnand3
|
|
||||||
from pinvbuf import pinvbuf
|
from pinvbuf import pinvbuf
|
||||||
from dff_inv import dff_inv
|
from dff_buf import dff_buf
|
||||||
from dff_inv_array import dff_inv_array
|
from dff_buf_array import dff_buf_array
|
||||||
import math
|
import math
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
|
|
@ -47,9 +48,7 @@ class control_logic(design.design):
|
||||||
""" Create layout and route between modules """
|
""" Create layout and route between modules """
|
||||||
self.place_instances()
|
self.place_instances()
|
||||||
self.route_all()
|
self.route_all()
|
||||||
|
|
||||||
#self.add_lvs_correspondence_points()
|
#self.add_lvs_correspondence_points()
|
||||||
|
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,28 +64,37 @@ class control_logic(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add all the required modules """
|
""" Add all the required modules """
|
||||||
|
|
||||||
dff = dff_inv()
|
dff = dff_buf()
|
||||||
dff_height = dff.height
|
dff_height = dff.height
|
||||||
|
|
||||||
self.ctrl_dff_array = dff_inv_array(rows=self.num_control_signals,columns=1)
|
self.ctrl_dff_array = dff_buf_array(rows=self.num_control_signals,columns=1)
|
||||||
self.add_mod(self.ctrl_dff_array)
|
self.add_mod(self.ctrl_dff_array)
|
||||||
|
|
||||||
self.nand2 = pnand2(height=dff_height)
|
self.and2 = pand2(size=4,height=dff_height)
|
||||||
self.add_mod(self.nand2)
|
self.add_mod(self.and2)
|
||||||
self.nand3 = pnand3(height=dff_height)
|
|
||||||
self.add_mod(self.nand3)
|
|
||||||
|
|
||||||
# Special gates: inverters for buffering
|
# Special gates: inverters for buffering
|
||||||
# Size the clock for the number of rows (fanout)
|
# Size the clock for the number of rows (fanout)
|
||||||
clock_driver_size = max(1,int(self.num_rows/4))
|
clock_driver_size = max(1,int(self.num_rows/4))
|
||||||
self.clkbuf = pinvbuf(clock_driver_size,height=dff_height)
|
self.clkbuf = pbuf(size=clock_driver_size, height=dff_height)
|
||||||
self.add_mod(self.clkbuf)
|
self.add_mod(self.clkbuf)
|
||||||
|
|
||||||
|
self.buf16 = pbuf(size=16, height=dff_height)
|
||||||
|
self.add_mod(self.buf16)
|
||||||
|
|
||||||
|
self.buf8 = pbuf(size=8, height=dff_height)
|
||||||
|
self.add_mod(self.buf8)
|
||||||
|
|
||||||
self.inv = self.inv1 = pinv(size=1, height=dff_height)
|
self.inv = self.inv1 = pinv(size=1, height=dff_height)
|
||||||
self.add_mod(self.inv1)
|
self.add_mod(self.inv1)
|
||||||
self.inv2 = pinv(size=4, height=dff_height)
|
|
||||||
self.add_mod(self.inv2)
|
self.inv8 = pinv(size=8, height=dff_height)
|
||||||
self.inv8 = pinv(size=16, height=dff_height)
|
|
||||||
self.add_mod(self.inv8)
|
self.add_mod(self.inv8)
|
||||||
|
|
||||||
|
# self.inv2 = pinv(size=4, height=dff_height)
|
||||||
|
# self.add_mod(self.inv2)
|
||||||
|
#self.inv16 = pinv(size=16, height=dff_height)
|
||||||
|
#self.add_mod(self.inv16)
|
||||||
|
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
|
|
@ -127,20 +135,22 @@ class control_logic(design.design):
|
||||||
|
|
||||||
# list of output control signals (for making a vertical bus)
|
# list of output control signals (for making a vertical bus)
|
||||||
if self.port_type == "rw":
|
if self.port_type == "rw":
|
||||||
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "we", "cs"]
|
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "we", "clk_buf", "we_bar", "cs"]
|
||||||
|
elif self.port_type == "r":
|
||||||
|
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs_bar", "cs"]
|
||||||
else:
|
else:
|
||||||
self.internal_bus_list = ["clk_buf", "clk_buf_bar", "cs"]
|
self.internal_bus_list = ["gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
|
||||||
# leave space for the bus plus one extra space
|
# leave space for the bus plus one extra space
|
||||||
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
|
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
|
||||||
|
|
||||||
# Outputs to the bank
|
# Outputs to the bank
|
||||||
if self.port_type == "r":
|
if self.port_type == "rw":
|
||||||
self.output_list = ["s_en"]
|
self.output_list = ["s_en", "w_en", "p_en_bar"]
|
||||||
elif self.port_type == "w":
|
elif self.port_type == "r":
|
||||||
self.output_list = ["w_en"]
|
self.output_list = ["s_en", "p_en_bar"]
|
||||||
else:
|
else:
|
||||||
self.output_list = ["s_en", "w_en"]
|
self.output_list = ["w_en"]
|
||||||
self.output_list.append("clk_buf_bar")
|
self.output_list.append("wl_en")
|
||||||
self.output_list.append("clk_buf")
|
self.output_list.append("clk_buf")
|
||||||
|
|
||||||
self.supply_list = ["vdd", "gnd"]
|
self.supply_list = ["vdd", "gnd"]
|
||||||
|
|
@ -157,11 +167,16 @@ class control_logic(design.design):
|
||||||
def create_instances(self):
|
def create_instances(self):
|
||||||
""" Create all the instances """
|
""" Create all the instances """
|
||||||
self.create_dffs()
|
self.create_dffs()
|
||||||
self.create_clk_row()
|
self.create_clk_buf_row()
|
||||||
|
self.create_gated_clk_bar_row()
|
||||||
|
self.create_gated_clk_buf_row()
|
||||||
|
self.create_wlen_row()
|
||||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
self.create_we_row()
|
self.create_wen_row()
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if self.port_type == "rw":
|
||||||
self.create_rbl_in_row()
|
self.create_rbl_in_row()
|
||||||
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
|
self.create_pen_row()
|
||||||
self.create_sen_row()
|
self.create_sen_row()
|
||||||
self.create_rbl()
|
self.create_rbl()
|
||||||
|
|
||||||
|
|
@ -175,19 +190,33 @@ class control_logic(design.design):
|
||||||
# Add the control flops on the left of the bus
|
# Add the control flops on the left of the bus
|
||||||
self.place_dffs()
|
self.place_dffs()
|
||||||
|
|
||||||
|
# All of the control logic is placed to the right of the DFFs and bus
|
||||||
|
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width
|
||||||
|
|
||||||
row = 0
|
row = 0
|
||||||
# Add the logic on the right of the bus
|
# Add the logic on the right of the bus
|
||||||
self.place_clk_row(row=row) # clk is a double-high cell
|
self.place_clk_buf_row(row)
|
||||||
row += 2
|
row += 1
|
||||||
|
self.place_gated_clk_bar_row(row)
|
||||||
|
row += 1
|
||||||
|
self.place_gated_clk_buf_row(row)
|
||||||
|
row += 1
|
||||||
|
self.place_wlen_row(row)
|
||||||
|
row += 1
|
||||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
self.place_we_row(row=row)
|
self.place_wen_row(row)
|
||||||
height = self.w_en_inst.uy()
|
height = self.w_en_inst.uy()
|
||||||
control_center_y = self.w_en_inst.uy()
|
control_center_y = self.w_en_inst.uy()
|
||||||
row += 1
|
row += 1
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if self.port_type == "rw":
|
||||||
self.place_rbl_in_row(row=row)
|
self.place_rbl_in_row(row)
|
||||||
self.place_sen_row(row=row+1)
|
row += 1
|
||||||
self.place_rbl(row=row+2)
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
|
self.place_pen_row(row)
|
||||||
|
row += 1
|
||||||
|
self.place_sen_row(row)
|
||||||
|
row += 1
|
||||||
|
self.place_rbl(row)
|
||||||
height = self.rbl_inst.uy()
|
height = self.rbl_inst.uy()
|
||||||
control_center_y = self.rbl_inst.by()
|
control_center_y = self.rbl_inst.by()
|
||||||
|
|
||||||
|
|
@ -197,122 +226,350 @@ class control_logic(design.design):
|
||||||
# Extra pitch on top and right
|
# Extra pitch on top and right
|
||||||
self.height = height + 2*self.m1_pitch
|
self.height = height + 2*self.m1_pitch
|
||||||
# Max of modules or logic rows
|
# Max of modules or logic rows
|
||||||
|
self.width = max([inst.rx() for inst in self.row_end_inst])
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch
|
self.width = max(self.rbl_inst.rx() , self.width)
|
||||||
else:
|
self.width += self.m2_pitch
|
||||||
self.width = max([inst.rx() for inst in self.row_end_inst]) + self.m2_pitch
|
|
||||||
|
|
||||||
def route_all(self):
|
def route_all(self):
|
||||||
""" Routing between modules """
|
""" Routing between modules """
|
||||||
self.route_rails()
|
self.route_rails()
|
||||||
self.route_dffs()
|
self.route_dffs()
|
||||||
|
self.route_wlen()
|
||||||
if (self.port_type == "rw") or (self.port_type == "w"):
|
if (self.port_type == "rw") or (self.port_type == "w"):
|
||||||
self.route_wen()
|
self.route_wen()
|
||||||
if (self.port_type == "rw") or (self.port_type == "r"):
|
if (self.port_type == "rw") or (self.port_type == "r"):
|
||||||
self.route_rbl_in()
|
self.route_rbl_in()
|
||||||
|
self.route_pen()
|
||||||
self.route_sen()
|
self.route_sen()
|
||||||
self.route_clk()
|
self.route_clk_buf()
|
||||||
|
self.route_gated_clk_bar()
|
||||||
|
self.route_gated_clk_buf()
|
||||||
self.route_supply()
|
self.route_supply()
|
||||||
|
|
||||||
|
|
||||||
def create_rbl(self):
|
def create_rbl(self):
|
||||||
""" Create the replica bitline """
|
""" Create the replica bitline """
|
||||||
|
if self.port_type == "r":
|
||||||
|
input_name = "gated_clk_bar"
|
||||||
|
else:
|
||||||
|
input_name = "rbl_in"
|
||||||
self.rbl_inst=self.add_inst(name="replica_bitline",
|
self.rbl_inst=self.add_inst(name="replica_bitline",
|
||||||
mod=self.replica_bitline)
|
mod=self.replica_bitline)
|
||||||
self.connect_inst(["rbl_in", "pre_s_en", "vdd", "gnd"])
|
self.connect_inst([input_name, "pre_s_en", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_rbl(self,row):
|
def place_rbl(self,row):
|
||||||
""" Place the replica bitline """
|
""" Place the replica bitline """
|
||||||
y_off = row * self.inv1.height + 2*self.m1_pitch
|
y_off = row * self.and2.height + 2*self.m1_pitch
|
||||||
|
|
||||||
# Add the RBL above the rows
|
# Add the RBL above the rows
|
||||||
# Add to the right of the control rows and routing channel
|
# Add to the right of the control rows and routing channel
|
||||||
self.replica_bitline_offset = vector(0, y_off)
|
offset = vector(0, y_off)
|
||||||
self.rbl_inst.place(self.replica_bitline_offset)
|
self.rbl_inst.place(offset)
|
||||||
|
|
||||||
|
|
||||||
def create_clk_row(self):
|
def create_clk_buf_row(self):
|
||||||
""" Create the multistage clock buffer """
|
""" Create the multistage and gated clock buffer """
|
||||||
self.clkbuf_inst = self.add_inst(name="clkbuf",
|
self.clkbuf_inst = self.add_inst(name="clkbuf",
|
||||||
mod=self.clkbuf)
|
mod=self.clkbuf)
|
||||||
self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"])
|
self.connect_inst(["clk","clk_buf","vdd","gnd"])
|
||||||
|
|
||||||
def place_clk_row(self,row):
|
|
||||||
""" Place the multistage clock buffer below the control flops """
|
|
||||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
clkbuf_offset = vector(x_off,y_off)
|
|
||||||
self.clkbuf_inst.place(clkbuf_offset)
|
|
||||||
self.row_end_inst.append(self.clkbuf_inst)
|
|
||||||
|
|
||||||
|
def place_clk_buf_row(self,row):
|
||||||
|
""" Place the multistage clock buffer below the control flops """
|
||||||
|
x_off = self.control_x_offset
|
||||||
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
|
offset = vector(x_off,y_off)
|
||||||
|
self.clkbuf_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.clkbuf_inst)
|
||||||
|
|
||||||
|
def route_clk_buf(self):
|
||||||
|
clk_pin = self.clkbuf_inst.get_pin("A")
|
||||||
|
clk_pos = clk_pin.center()
|
||||||
|
self.add_layout_pin_segment_center(text="clk",
|
||||||
|
layer="metal2",
|
||||||
|
start=clk_pos,
|
||||||
|
end=clk_pos.scale(1,0))
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=clk_pos)
|
||||||
|
|
||||||
|
|
||||||
|
clkbuf_map = zip(["Z"], ["clk_buf"])
|
||||||
|
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
||||||
|
# The pin is on M1, so we need another via as well
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=self.clkbuf_inst.get_pin("Z").center())
|
||||||
|
|
||||||
|
|
||||||
|
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
|
||||||
|
|
||||||
|
def create_gated_clk_bar_row(self):
|
||||||
|
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
|
||||||
|
mod=self.inv)
|
||||||
|
self.connect_inst(["clk_buf","clk_bar","vdd","gnd"])
|
||||||
|
|
||||||
|
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
|
||||||
|
mod=self.and2)
|
||||||
|
self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"])
|
||||||
|
|
||||||
|
def place_gated_clk_bar_row(self,row):
|
||||||
|
""" Place the gated clk logic below the control flops """
|
||||||
|
x_off = self.control_x_offset
|
||||||
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
|
offset = vector(x_off,y_off)
|
||||||
|
self.clk_bar_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
x_off += self.inv.width
|
||||||
|
|
||||||
|
offset = vector(x_off,y_off)
|
||||||
|
self.gated_clk_bar_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.gated_clk_bar_inst)
|
||||||
|
|
||||||
|
def route_gated_clk_bar(self):
|
||||||
|
clkbuf_map = zip(["A"], ["clk_buf"])
|
||||||
|
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
|
||||||
|
|
||||||
|
out_pos = self.clk_bar_inst.get_pin("Z").center()
|
||||||
|
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
|
||||||
|
mid1 = vector(in_pos.x,out_pos.y)
|
||||||
|
self.add_path("metal1",[out_pos, mid1, in_pos])
|
||||||
|
|
||||||
|
# This is the second gate over, so it needs to be on M3
|
||||||
|
clkbuf_map = zip(["A"], ["cs"])
|
||||||
|
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
||||||
|
# The pin is on M1, so we need another via as well
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=self.gated_clk_bar_inst.get_pin("A").center())
|
||||||
|
|
||||||
|
|
||||||
|
# This is the second gate over, so it needs to be on M3
|
||||||
|
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
|
||||||
|
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
||||||
|
# The pin is on M1, so we need another via as well
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=self.gated_clk_bar_inst.get_pin("Z").center())
|
||||||
|
|
||||||
|
def create_gated_clk_buf_row(self):
|
||||||
|
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
|
||||||
|
mod=self.and2)
|
||||||
|
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"])
|
||||||
|
|
||||||
|
def place_gated_clk_buf_row(self,row):
|
||||||
|
""" Place the gated clk logic below the control flops """
|
||||||
|
x_off = self.control_x_offset
|
||||||
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
|
offset = vector(x_off,y_off)
|
||||||
|
self.gated_clk_buf_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.gated_clk_buf_inst)
|
||||||
|
|
||||||
|
def route_gated_clk_buf(self):
|
||||||
|
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
|
||||||
|
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
|
||||||
|
|
||||||
|
|
||||||
|
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
|
||||||
|
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
||||||
|
# The pin is on M1, so we need another via as well
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=self.gated_clk_buf_inst.get_pin("Z").center())
|
||||||
|
|
||||||
|
def create_wlen_row(self):
|
||||||
|
# input pre_p_en, output: wl_en
|
||||||
|
self.wl_en_inst=self.add_inst(name="buf_wl_en",
|
||||||
|
mod=self.buf16)
|
||||||
|
self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_wlen_row(self, row):
|
||||||
|
x_off = self.control_x_offset
|
||||||
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
|
offset = vector(x_off, y_off)
|
||||||
|
self.wl_en_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.wl_en_inst)
|
||||||
|
|
||||||
|
def route_wlen(self):
|
||||||
|
wlen_map = zip(["A"], ["gated_clk_bar"])
|
||||||
|
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets)
|
||||||
|
self.connect_output(self.wl_en_inst, "Z", "wl_en")
|
||||||
|
|
||||||
def create_rbl_in_row(self):
|
def create_rbl_in_row(self):
|
||||||
self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar",
|
|
||||||
mod=self.nand2)
|
|
||||||
self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"])
|
|
||||||
|
|
||||||
# input: rbl_in_bar, output: rbl_in
|
|
||||||
self.rbl_in_inst=self.add_inst(name="inv_rbl_in",
|
|
||||||
mod=self.inv1)
|
|
||||||
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
|
|
||||||
|
|
||||||
|
# input: gated_clk_bar, we_bar, output: rbl_in
|
||||||
|
self.rbl_in_inst=self.add_inst(name="and2_rbl_in",
|
||||||
|
mod=self.and2)
|
||||||
|
self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"])
|
||||||
|
|
||||||
def place_rbl_in_row(self,row):
|
def place_rbl_in_row(self,row):
|
||||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
x_off = self.control_x_offset
|
||||||
(y_off,mirror)=self.get_offset(row)
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
|
offset = vector(x_off, y_off)
|
||||||
|
self.rbl_in_inst.place(offset, mirror)
|
||||||
|
|
||||||
self.rbl_in_bar_offset = vector(x_off, y_off)
|
|
||||||
self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset,
|
|
||||||
mirror=mirror)
|
|
||||||
x_off += self.nand2.width
|
|
||||||
|
|
||||||
self.rbl_in_offset = vector(x_off, y_off)
|
|
||||||
self.rbl_in_inst.place(offset=self.rbl_in_offset,
|
|
||||||
mirror=mirror)
|
|
||||||
self.row_end_inst.append(self.rbl_in_inst)
|
self.row_end_inst.append(self.rbl_in_inst)
|
||||||
|
|
||||||
|
def route_rbl_in(self):
|
||||||
|
""" Connect the logic for the rbl_in generation """
|
||||||
|
|
||||||
|
if self.port_type == "rw":
|
||||||
|
input_name = "we_bar"
|
||||||
|
# Connect the NAND gate inputs to the bus
|
||||||
|
rbl_in_map = zip(["A", "B"], ["gated_clk_bar", "we_bar"])
|
||||||
|
self.connect_vertical_bus(rbl_in_map, self.rbl_in_inst, self.rail_offsets)
|
||||||
|
|
||||||
|
|
||||||
|
# Connect the output of the precharge enable to the RBL input
|
||||||
|
if self.port_type == "rw":
|
||||||
|
out_pos = self.rbl_in_inst.get_pin("Z").center()
|
||||||
|
else:
|
||||||
|
out_pos = vector(self.rail_offsets["gated_clk_bar"].x, self.rbl_inst.by()-3*self.m2_pitch)
|
||||||
|
in_pos = self.rbl_inst.get_pin("en").center()
|
||||||
|
mid1 = vector(in_pos.x,out_pos.y)
|
||||||
|
self.add_wire(("metal3","via2","metal2"),[out_pos, mid1, in_pos])
|
||||||
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=out_pos,
|
||||||
|
rotate=90)
|
||||||
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
|
offset=out_pos,
|
||||||
|
rotate=90)
|
||||||
|
|
||||||
|
def create_pen_row(self):
|
||||||
|
if self.port_type == "rw":
|
||||||
|
# input: gated_clk_bar, we_bar, output: pre_p_en
|
||||||
|
self.pre_p_en_inst=self.add_inst(name="and2_pre_p_en",
|
||||||
|
mod=self.and2)
|
||||||
|
self.connect_inst(["gated_clk_buf", "we_bar", "pre_p_en", "vdd", "gnd"])
|
||||||
|
input_name = "pre_p_en"
|
||||||
|
else:
|
||||||
|
input_name = "gated_clk_buf"
|
||||||
|
|
||||||
|
# input: pre_p_en, output: p_en_bar
|
||||||
|
self.p_en_bar_inst=self.add_inst(name="inv_p_en_bar",
|
||||||
|
mod=self.inv8)
|
||||||
|
self.connect_inst([input_name, "p_en_bar", "vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
|
def place_pen_row(self,row):
|
||||||
|
x_off = self.control_x_offset
|
||||||
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
|
if self.port_type == "rw":
|
||||||
|
offset = vector(x_off, y_off)
|
||||||
|
self.pre_p_en_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
x_off += self.and2.width
|
||||||
|
|
||||||
|
offset = vector(x_off,y_off)
|
||||||
|
self.p_en_bar_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.p_en_bar_inst)
|
||||||
|
|
||||||
|
def route_pen(self):
|
||||||
|
if self.port_type == "rw":
|
||||||
|
# Connect the NAND gate inputs to the bus
|
||||||
|
pre_p_en_in_map = zip(["A", "B"], ["gated_clk_buf", "we_bar"])
|
||||||
|
self.connect_vertical_bus(pre_p_en_in_map, self.pre_p_en_inst, self.rail_offsets)
|
||||||
|
|
||||||
|
out_pos = self.pre_p_en_inst.get_pin("Z").center()
|
||||||
|
in_pos = self.p_en_bar_inst.get_pin("A").lc()
|
||||||
|
mid1 = vector(out_pos.x,in_pos.y)
|
||||||
|
self.add_wire(("metal1","via1","metal2"),[out_pos,mid1,in_pos])
|
||||||
|
else:
|
||||||
|
in_map = zip(["A"], ["gated_clk_buf"])
|
||||||
|
self.connect_vertical_bus(in_map, self.p_en_bar_inst, self.rail_offsets)
|
||||||
|
|
||||||
|
self.connect_output(self.p_en_bar_inst, "Z", "p_en_bar")
|
||||||
|
|
||||||
def create_sen_row(self):
|
def create_sen_row(self):
|
||||||
""" Create the sense enable buffer. """
|
""" Create the sense enable buffer. """
|
||||||
# input: pre_s_en, output: pre_s_en_bar
|
# BUFFER FOR S_EN
|
||||||
self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar",
|
# input: pre_s_en, output: s_en
|
||||||
mod=self.inv2)
|
self.s_en_inst=self.add_inst(name="buf_s_en",
|
||||||
self.connect_inst(["pre_s_en", "pre_s_en_bar", "vdd", "gnd"])
|
mod=self.buf8)
|
||||||
|
self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"])
|
||||||
# BUFFER INVERTERS FOR S_EN
|
|
||||||
# input: input: pre_s_en_bar, output: s_en
|
|
||||||
self.s_en_inst=self.add_inst(name="inv_s_en",
|
|
||||||
mod=self.inv8)
|
|
||||||
self.connect_inst(["pre_s_en_bar", "s_en", "vdd", "gnd"])
|
|
||||||
|
|
||||||
def place_sen_row(self,row):
|
def place_sen_row(self,row):
|
||||||
"""
|
"""
|
||||||
The sense enable buffer gets placed to the far right of the
|
The sense enable buffer gets placed to the far right of the
|
||||||
row.
|
row.
|
||||||
"""
|
"""
|
||||||
x_off = self.ctrl_dff_array.width + self.internal_bus_width
|
x_off = self.control_x_offset
|
||||||
(y_off,mirror)=self.get_offset(row)
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
self.pre_s_en_bar_offset = vector(x_off, y_off)
|
offset = vector(x_off, y_off)
|
||||||
self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset,
|
self.s_en_inst.place(offset, mirror)
|
||||||
mirror=mirror)
|
|
||||||
x_off += self.inv2.width
|
|
||||||
|
|
||||||
self.s_en_offset = vector(x_off, y_off)
|
|
||||||
self.s_en_inst.place(offset=self.s_en_offset,
|
|
||||||
mirror=mirror)
|
|
||||||
self.row_end_inst.append(self.s_en_inst)
|
self.row_end_inst.append(self.s_en_inst)
|
||||||
|
|
||||||
|
|
||||||
|
def route_sen(self):
|
||||||
|
|
||||||
|
out_pos = self.rbl_inst.get_pin("out").bc()
|
||||||
|
in_pos = self.s_en_inst.get_pin("A").lc()
|
||||||
|
mid1 = vector(out_pos.x,in_pos.y)
|
||||||
|
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
|
||||||
|
|
||||||
|
self.connect_output(self.s_en_inst, "Z", "s_en")
|
||||||
|
|
||||||
|
|
||||||
|
def create_wen_row(self):
|
||||||
|
# input: we (or cs) output: w_en
|
||||||
|
if self.port_type == "rw":
|
||||||
|
input_name = "we"
|
||||||
|
else:
|
||||||
|
# No we for write-only reports, so use cs
|
||||||
|
input_name = "cs"
|
||||||
|
|
||||||
|
# BUFFER FOR W_EN
|
||||||
|
self.w_en_inst = self.add_inst(name="buf_w_en_buf",
|
||||||
|
mod=self.buf8)
|
||||||
|
self.connect_inst([input_name, "w_en", "vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
|
def place_wen_row(self,row):
|
||||||
|
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
|
||||||
|
(y_off,mirror)=self.get_offset(row)
|
||||||
|
|
||||||
|
offset = vector(x_off, y_off)
|
||||||
|
self.w_en_inst.place(offset, mirror)
|
||||||
|
|
||||||
|
self.row_end_inst.append(self.w_en_inst)
|
||||||
|
|
||||||
|
def route_wen(self):
|
||||||
|
|
||||||
|
if self.port_type == "rw":
|
||||||
|
input_name = "we"
|
||||||
|
else:
|
||||||
|
# No we for write-only reports, so use cs
|
||||||
|
input_name = "cs"
|
||||||
|
|
||||||
|
wen_map = zip(["A"], [input_name])
|
||||||
|
self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets)
|
||||||
|
|
||||||
|
self.connect_output(self.w_en_inst, "Z", "w_en")
|
||||||
|
|
||||||
|
def create_dffs(self):
|
||||||
|
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
|
||||||
|
mod=self.ctrl_dff_array)
|
||||||
|
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
|
||||||
|
|
||||||
|
def place_dffs(self):
|
||||||
|
self.ctrl_dff_inst.place(vector(0,0))
|
||||||
|
|
||||||
def route_dffs(self):
|
def route_dffs(self):
|
||||||
""" Route the input inverters """
|
if self.port_type == "rw":
|
||||||
|
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
|
||||||
if self.port_type == "r":
|
elif self.port_type == "r":
|
||||||
control_inputs = ["cs"]
|
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
|
||||||
else:
|
else:
|
||||||
control_inputs = ["cs", "we"]
|
dff_out_map = zip(["dout_bar_0"], ["cs"])
|
||||||
dff_out_map = zip(["dout_bar_{}".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs)
|
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
||||||
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
|
|
||||||
|
|
||||||
# Connect the clock rail to the other clock rail
|
# Connect the clock rail to the other clock rail
|
||||||
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
|
||||||
|
|
@ -327,207 +584,18 @@ class control_logic(design.design):
|
||||||
if (self.port_type == "rw"):
|
if (self.port_type == "rw"):
|
||||||
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
|
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
|
||||||
|
|
||||||
|
|
||||||
def create_dffs(self):
|
|
||||||
""" Add the three input DFFs (with inverters) """
|
|
||||||
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
|
|
||||||
mod=self.ctrl_dff_array)
|
|
||||||
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
|
|
||||||
|
|
||||||
def place_dffs(self):
|
|
||||||
""" Place the input DFFs (with inverters) """
|
|
||||||
self.ctrl_dff_inst.place(vector(0,0))
|
|
||||||
|
|
||||||
|
|
||||||
def get_offset(self,row):
|
def get_offset(self,row):
|
||||||
""" Compute the y-offset and mirroring """
|
""" Compute the y-offset and mirroring """
|
||||||
y_off = row*self.inv1.height
|
y_off = row*self.and2.height
|
||||||
if row % 2:
|
if row % 2:
|
||||||
y_off += self.inv1.height
|
y_off += self.and2.height
|
||||||
mirror="MX"
|
mirror="MX"
|
||||||
else:
|
else:
|
||||||
mirror="R0"
|
mirror="R0"
|
||||||
|
|
||||||
return (y_off,mirror)
|
return (y_off,mirror)
|
||||||
|
|
||||||
def create_we_row(self):
|
|
||||||
# input: WE, CS output: w_en_bar
|
|
||||||
if self.port_type == "rw":
|
|
||||||
nand_mod = self.nand3
|
|
||||||
temp = ["clk_buf_bar", "cs", "we", "w_en_bar", "vdd", "gnd"]
|
|
||||||
else:
|
|
||||||
nand_mod = self.nand2
|
|
||||||
temp = ["clk_buf_bar", "cs", "w_en_bar", "vdd", "gnd"]
|
|
||||||
|
|
||||||
self.w_en_bar_inst = self.add_inst(name="nand3_w_en_bar",
|
|
||||||
mod=nand_mod)
|
|
||||||
self.connect_inst(temp)
|
|
||||||
|
|
||||||
# input: w_en_bar, output: pre_w_en
|
|
||||||
self.pre_w_en_inst = self.add_inst(name="inv_pre_w_en",
|
|
||||||
mod=self.inv1)
|
|
||||||
self.connect_inst(["w_en_bar", "pre_w_en", "vdd", "gnd"])
|
|
||||||
|
|
||||||
# BUFFER INVERTERS FOR W_EN
|
|
||||||
self.pre_w_en_bar_inst = self.add_inst(name="inv_pre_w_en_bar",
|
|
||||||
mod=self.inv2)
|
|
||||||
self.connect_inst(["pre_w_en", "pre_w_en_bar", "vdd", "gnd"])
|
|
||||||
|
|
||||||
self.w_en_inst = self.add_inst(name="inv_w_en2",
|
|
||||||
mod=self.inv8)
|
|
||||||
self.connect_inst(["pre_w_en_bar", "w_en", "vdd", "gnd"])
|
|
||||||
|
|
||||||
|
|
||||||
def place_we_row(self,row):
|
|
||||||
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
|
|
||||||
(y_off,mirror)=self.get_offset(row)
|
|
||||||
|
|
||||||
w_en_bar_offset = vector(x_off, y_off)
|
|
||||||
self.w_en_bar_inst.place(offset=w_en_bar_offset,
|
|
||||||
mirror=mirror)
|
|
||||||
if self.port_type == "rw":
|
|
||||||
x_off += self.nand3.width
|
|
||||||
else:
|
|
||||||
x_off += self.nand2.width
|
|
||||||
|
|
||||||
pre_w_en_offset = vector(x_off, y_off)
|
|
||||||
self.pre_w_en_inst.place(offset=pre_w_en_offset,
|
|
||||||
mirror=mirror)
|
|
||||||
x_off += self.inv1.width
|
|
||||||
|
|
||||||
pre_w_en_bar_offset = vector(x_off, y_off)
|
|
||||||
self.pre_w_en_bar_inst.place(offset=pre_w_en_bar_offset,
|
|
||||||
mirror=mirror)
|
|
||||||
x_off += self.inv2.width
|
|
||||||
|
|
||||||
w_en_offset = vector(x_off, y_off)
|
|
||||||
self.w_en_inst.place(offset=w_en_offset,
|
|
||||||
mirror=mirror)
|
|
||||||
x_off += self.inv8.width
|
|
||||||
|
|
||||||
self.row_end_inst.append(self.w_en_inst)
|
|
||||||
|
|
||||||
|
|
||||||
def route_rbl_in(self):
|
|
||||||
""" Connect the logic for the rbl_in generation """
|
|
||||||
rbl_in_map = zip(["A", "B"], ["clk_buf_bar", "cs"])
|
|
||||||
self.connect_vertical_bus(rbl_in_map, self.rbl_in_bar_inst, self.rail_offsets)
|
|
||||||
|
|
||||||
# Connect the NAND3 output to the inverter
|
|
||||||
# The pins are assumed to extend all the way to the cell edge
|
|
||||||
rbl_in_bar_pos = self.rbl_in_bar_inst.get_pin("Z").center()
|
|
||||||
inv_in_pos = self.rbl_in_inst.get_pin("A").center()
|
|
||||||
mid1 = vector(inv_in_pos.x,rbl_in_bar_pos.y)
|
|
||||||
self.add_path("metal1",[rbl_in_bar_pos,mid1,inv_in_pos])
|
|
||||||
|
|
||||||
# Connect the output to the RBL
|
|
||||||
rbl_out_pos = self.rbl_in_inst.get_pin("Z").center()
|
|
||||||
rbl_in_pos = self.rbl_inst.get_pin("en").center()
|
|
||||||
mid1 = vector(rbl_in_pos.x,rbl_out_pos.y)
|
|
||||||
self.add_wire(("metal3","via2","metal2"),[rbl_out_pos,mid1,rbl_in_pos])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rbl_out_pos,
|
|
||||||
rotate=90)
|
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
||||||
offset=rbl_out_pos,
|
|
||||||
rotate=90)
|
|
||||||
|
|
||||||
|
|
||||||
def connect_rail_from_right(self,inst, pin, rail):
|
|
||||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
|
||||||
in_pos = inst.get_pin(pin).center()
|
|
||||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
|
||||||
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail_pos,
|
|
||||||
rotate=90)
|
|
||||||
|
|
||||||
def connect_rail_from_right_m2m3(self,inst, pin, rail):
|
|
||||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
|
||||||
in_pos = inst.get_pin(pin).center()
|
|
||||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
|
||||||
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
|
|
||||||
# Bring it up to M2 for M2/M3 routing
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=in_pos,
|
|
||||||
rotate=90)
|
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
||||||
offset=in_pos,
|
|
||||||
rotate=90)
|
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
||||||
offset=rail_pos,
|
|
||||||
rotate=90)
|
|
||||||
|
|
||||||
|
|
||||||
def connect_rail_from_left(self,inst, pin, rail):
|
|
||||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
|
||||||
in_pos = inst.get_pin(pin).lc()
|
|
||||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
|
||||||
self.add_wire(("metal1","via1","metal2"),[in_pos, rail_pos])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail_pos,
|
|
||||||
rotate=90)
|
|
||||||
|
|
||||||
def connect_rail_from_left_m2m3(self,inst, pin, rail):
|
|
||||||
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """
|
|
||||||
in_pos = inst.get_pin(pin).lc()
|
|
||||||
rail_pos = vector(self.rail_offsets[rail].x, in_pos.y)
|
|
||||||
self.add_wire(("metal3","via2","metal2"),[in_pos, rail_pos])
|
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
||||||
offset=in_pos,
|
|
||||||
rotate=90)
|
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
||||||
offset=rail_pos,
|
|
||||||
rotate=90)
|
|
||||||
|
|
||||||
|
|
||||||
def route_wen(self):
|
|
||||||
if self.port_type == "rw":
|
|
||||||
wen_map = zip(["A", "B", "C"], ["clk_buf_bar", "cs", "we"])
|
|
||||||
else:
|
|
||||||
wen_map = zip(["A", "B"], ["clk_buf_bar", "cs"])
|
|
||||||
self.connect_vertical_bus(wen_map, self.w_en_bar_inst, self.rail_offsets)
|
|
||||||
|
|
||||||
# Connect the NAND3 output to the inverter
|
|
||||||
# The pins are assumed to extend all the way to the cell edge
|
|
||||||
w_en_bar_pos = self.w_en_bar_inst.get_pin("Z").center()
|
|
||||||
inv_in_pos = self.pre_w_en_inst.get_pin("A").center()
|
|
||||||
mid1 = vector(inv_in_pos.x,w_en_bar_pos.y)
|
|
||||||
self.add_path("metal1",[w_en_bar_pos,mid1,inv_in_pos])
|
|
||||||
|
|
||||||
self.add_path("metal1",[self.pre_w_en_inst.get_pin("Z").center(), self.pre_w_en_bar_inst.get_pin("A").center()])
|
|
||||||
self.add_path("metal1",[self.pre_w_en_bar_inst.get_pin("Z").center(), self.w_en_inst.get_pin("A").center()])
|
|
||||||
|
|
||||||
self.connect_output(self.w_en_inst, "Z", "w_en")
|
|
||||||
|
|
||||||
def route_sen(self):
|
|
||||||
rbl_out_pos = self.rbl_inst.get_pin("out").bc()
|
|
||||||
in_pos = self.pre_s_en_bar_inst.get_pin("A").lc()
|
|
||||||
mid1 = vector(rbl_out_pos.x,in_pos.y)
|
|
||||||
self.add_wire(("metal1","via1","metal2"),[rbl_out_pos,mid1,in_pos])
|
|
||||||
#s_en_pos = self.s_en.get_pin("Z").lc()
|
|
||||||
|
|
||||||
self.add_path("metal1",[self.pre_s_en_bar_inst.get_pin("Z").center(), self.s_en_inst.get_pin("A").center()])
|
|
||||||
|
|
||||||
self.connect_output(self.s_en_inst, "Z", "s_en")
|
|
||||||
|
|
||||||
def route_clk(self):
|
|
||||||
""" Route the clk and clk_buf_bar signal internally """
|
|
||||||
|
|
||||||
clk_pin = self.clkbuf_inst.get_pin("A")
|
|
||||||
self.add_layout_pin_segment_center(text="clk",
|
|
||||||
layer="metal2",
|
|
||||||
start=clk_pin.bc(),
|
|
||||||
end=clk_pin.bc().scale(1,0))
|
|
||||||
|
|
||||||
clkbuf_map = zip(["Z", "Zb"], ["clk_buf", "clk_buf_bar"])
|
|
||||||
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
|
|
||||||
|
|
||||||
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Z", "clk_buf")
|
|
||||||
# self.connect_rail_from_right_m2m3(self.clkbuf_inst, "Zb", "clk_buf_bar")
|
|
||||||
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
|
|
||||||
self.connect_output(self.clkbuf_inst, "Zb", "clk_buf_bar")
|
|
||||||
|
|
||||||
def connect_output(self, inst, pin_name, out_name):
|
def connect_output(self, inst, pin_name, out_name):
|
||||||
""" Create an output pin on the right side from the pin of a given instance. """
|
""" Create an output pin on the right side from the pin of a given instance. """
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ class dff_array(design.design):
|
||||||
self.dff_insts={}
|
self.dff_insts={}
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
name = "dff_r{0}_c{1}".format(row,col)
|
||||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||||
mod=self.dff)
|
mod=self.dff)
|
||||||
self.connect_inst([self.get_din_name(row,col),
|
self.connect_inst([self.get_din_name(row,col),
|
||||||
|
|
@ -71,7 +71,7 @@ class dff_array(design.design):
|
||||||
def place_dff_array(self):
|
def place_dff_array(self):
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
name = "dff_r{0}_c{1}".format(row,col)
|
||||||
if (row % 2 == 0):
|
if (row % 2 == 0):
|
||||||
base = vector(col*self.dff.width,row*self.dff.height)
|
base = vector(col*self.dff.width,row*self.dff.height)
|
||||||
mirror = "R0"
|
mirror = "R0"
|
||||||
|
|
|
||||||
|
|
@ -102,8 +102,7 @@ class dff_buf(design.design):
|
||||||
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
|
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
|
||||||
mid1 = vector(mid_x_offset, q_pin.cy())
|
mid1 = vector(mid_x_offset, q_pin.cy())
|
||||||
mid2 = vector(mid_x_offset, a1_pin.cy())
|
mid2 = vector(mid_x_offset, a1_pin.cy())
|
||||||
self.add_path("metal3",
|
self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()])
|
||||||
[q_pin.center(), mid1, mid2, a1_pin.center()])
|
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
offset=q_pin.center())
|
offset=q_pin.center())
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
self.add_via_center(layers=("metal2","via2","metal3"),
|
||||||
|
|
@ -114,8 +113,10 @@ class dff_buf(design.design):
|
||||||
# Route inv1 z to inv2 a
|
# Route inv1 z to inv2 a
|
||||||
z1_pin = self.inv1_inst.get_pin("Z")
|
z1_pin = self.inv1_inst.get_pin("Z")
|
||||||
a2_pin = self.inv2_inst.get_pin("A")
|
a2_pin = self.inv2_inst.get_pin("A")
|
||||||
mid_point = vector(z1_pin.cx(), a2_pin.cy())
|
mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx())
|
||||||
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
|
self.mid_qb_pos = vector(mid_x_offset, z1_pin.cy())
|
||||||
|
mid2 = vector(mid_x_offset, a2_pin.cy())
|
||||||
|
self.add_path("metal1", [z1_pin.center(), self.mid_qb_pos, mid2, a2_pin.center()])
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
|
|
@ -150,18 +151,22 @@ class dff_buf(design.design):
|
||||||
height=din_pin.height())
|
height=din_pin.height())
|
||||||
|
|
||||||
dout_pin = self.inv2_inst.get_pin("Z")
|
dout_pin = self.inv2_inst.get_pin("Z")
|
||||||
|
mid_pos = dout_pin.center() + vector(self.m1_pitch,0)
|
||||||
|
q_pos = mid_pos - vector(0,self.m2_pitch)
|
||||||
self.add_layout_pin_rect_center(text="Q",
|
self.add_layout_pin_rect_center(text="Q",
|
||||||
layer="metal2",
|
layer="metal2",
|
||||||
offset=dout_pin.center())
|
offset=q_pos)
|
||||||
|
self.add_path("metal1", [dout_pin.center(), mid_pos, q_pos])
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
offset=dout_pin.center())
|
offset=q_pos)
|
||||||
|
|
||||||
dout_pin = self.inv2_inst.get_pin("A")
|
qb_pos = self.mid_qb_pos + vector(0,self.m2_pitch)
|
||||||
self.add_layout_pin_rect_center(text="Qb",
|
self.add_layout_pin_rect_center(text="Qb",
|
||||||
layer="metal2",
|
layer="metal2",
|
||||||
offset=dout_pin.center())
|
offset=qb_pos)
|
||||||
|
self.add_path("metal1", [self.mid_qb_pos, qb_pos])
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
offset=dout_pin.center())
|
offset=qb_pos)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ class dff_buf_array(design.design):
|
||||||
self.dff_insts={}
|
self.dff_insts={}
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
for col in range(self.columns):
|
for col in range(self.columns):
|
||||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
name = "dff_r{0}_c{1}".format(row,col)
|
||||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
self.dff_insts[row,col]=self.add_inst(name=name,
|
||||||
mod=self.dff)
|
mod=self.dff)
|
||||||
self.connect_inst([self.get_din_name(row,col),
|
self.connect_inst([self.get_din_name(row,col),
|
||||||
|
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
import debug
|
|
||||||
import design
|
|
||||||
from tech import drc
|
|
||||||
from math import log
|
|
||||||
from vector import vector
|
|
||||||
from globals import OPTS
|
|
||||||
import dff_inv
|
|
||||||
|
|
||||||
class dff_inv_array(design.design):
|
|
||||||
"""
|
|
||||||
This is a simple row (or multiple rows) of flops.
|
|
||||||
Unlike the data flops, these are never spaced out.
|
|
||||||
"""
|
|
||||||
unique_id = 1
|
|
||||||
|
|
||||||
def __init__(self, rows, columns, inv_size=2, name=""):
|
|
||||||
self.rows = rows
|
|
||||||
self.columns = columns
|
|
||||||
|
|
||||||
if name=="":
|
|
||||||
name = "dff_inv_array_{0}x{1}_{2}".format(rows, columns, dff_inv_array.unique_id)
|
|
||||||
dff_inv_array.unique_id += 1
|
|
||||||
design.design.__init__(self, name)
|
|
||||||
debug.info(1, "Creating {}".format(self.name))
|
|
||||||
self.inv_size = inv_size
|
|
||||||
|
|
||||||
self.create_netlist()
|
|
||||||
if not OPTS.netlist_only:
|
|
||||||
self.create_layout()
|
|
||||||
|
|
||||||
def create_netlist(self):
|
|
||||||
self.add_pins()
|
|
||||||
self.add_modules()
|
|
||||||
self.create_dff_array()
|
|
||||||
|
|
||||||
def create_layout(self):
|
|
||||||
self.width = self.columns * self.dff.width
|
|
||||||
self.height = self.rows * self.dff.height
|
|
||||||
|
|
||||||
self.place_dff_array()
|
|
||||||
self.add_layout_pins()
|
|
||||||
self.DRC_LVS()
|
|
||||||
|
|
||||||
def add_modules(self):
|
|
||||||
self.dff = dff_inv.dff_inv(self.inv_size)
|
|
||||||
self.add_mod(self.dff)
|
|
||||||
|
|
||||||
def add_pins(self):
|
|
||||||
for row in range(self.rows):
|
|
||||||
for col in range(self.columns):
|
|
||||||
self.add_pin(self.get_din_name(row,col))
|
|
||||||
for row in range(self.rows):
|
|
||||||
for col in range(self.columns):
|
|
||||||
self.add_pin(self.get_dout_name(row,col))
|
|
||||||
self.add_pin(self.get_dout_bar_name(row,col))
|
|
||||||
self.add_pin("clk")
|
|
||||||
self.add_pin("vdd")
|
|
||||||
self.add_pin("gnd")
|
|
||||||
|
|
||||||
def create_dff_array(self):
|
|
||||||
self.dff_insts={}
|
|
||||||
for row in range(self.rows):
|
|
||||||
for col in range(self.columns):
|
|
||||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
|
||||||
self.dff_insts[row,col]=self.add_inst(name=name,
|
|
||||||
mod=self.dff)
|
|
||||||
self.connect_inst([self.get_din_name(row,col),
|
|
||||||
self.get_dout_name(row,col),
|
|
||||||
self.get_dout_bar_name(row,col),
|
|
||||||
"clk",
|
|
||||||
"vdd",
|
|
||||||
"gnd"])
|
|
||||||
|
|
||||||
def place_dff_array(self):
|
|
||||||
for row in range(self.rows):
|
|
||||||
for col in range(self.columns):
|
|
||||||
name = "Xdff_r{0}_c{1}".format(row,col)
|
|
||||||
if (row % 2 == 0):
|
|
||||||
base = vector(col*self.dff.width,row*self.dff.height)
|
|
||||||
mirror = "R0"
|
|
||||||
else:
|
|
||||||
base = vector(col*self.dff.width,(row+1)*self.dff.height)
|
|
||||||
mirror = "MX"
|
|
||||||
self.dff_insts[row,col].place(offset=base,
|
|
||||||
mirror=mirror)
|
|
||||||
|
|
||||||
def get_din_name(self, row, col):
|
|
||||||
if self.columns == 1:
|
|
||||||
din_name = "din_{0}".format(row)
|
|
||||||
elif self.rows == 1:
|
|
||||||
din_name = "din_{0}".format(col)
|
|
||||||
else:
|
|
||||||
din_name = "din_{0}_{1}".format(row,col)
|
|
||||||
|
|
||||||
return din_name
|
|
||||||
|
|
||||||
def get_dout_name(self, row, col):
|
|
||||||
if self.columns == 1:
|
|
||||||
dout_name = "dout_{0}".format(row)
|
|
||||||
elif self.rows == 1:
|
|
||||||
dout_name = "dout_{0}".format(col)
|
|
||||||
else:
|
|
||||||
dout_name = "dout_{0}_{1}".format(row,col)
|
|
||||||
|
|
||||||
return dout_name
|
|
||||||
|
|
||||||
def get_dout_bar_name(self, row, col):
|
|
||||||
if self.columns == 1:
|
|
||||||
dout_bar_name = "dout_bar_{0}".format(row)
|
|
||||||
elif self.rows == 1:
|
|
||||||
dout_bar_name = "dout_bar_{0}".format(col)
|
|
||||||
else:
|
|
||||||
dout_bar_name = "dout_bar_{0}_{1}".format(row,col)
|
|
||||||
|
|
||||||
return dout_bar_name
|
|
||||||
|
|
||||||
def add_layout_pins(self):
|
|
||||||
for row in range(self.rows):
|
|
||||||
for col in range(self.columns):
|
|
||||||
# Adds power pin on left of row
|
|
||||||
vdd_pin=self.dff_insts[row,col].get_pin("vdd")
|
|
||||||
self.add_power_pin("vdd", vdd_pin.lc())
|
|
||||||
|
|
||||||
# Adds gnd pin on left of row
|
|
||||||
gnd_pin=self.dff_insts[row,col].get_pin("gnd")
|
|
||||||
self.add_power_pin("gnd", gnd_pin.lc())
|
|
||||||
|
|
||||||
|
|
||||||
for row in range(self.rows):
|
|
||||||
for col in range(self.columns):
|
|
||||||
din_pin = self.dff_insts[row,col].get_pin("D")
|
|
||||||
debug.check(din_pin.layer=="metal2","DFF D pin not on metal2")
|
|
||||||
self.add_layout_pin(text=self.get_din_name(row,col),
|
|
||||||
layer=din_pin.layer,
|
|
||||||
offset=din_pin.ll(),
|
|
||||||
width=din_pin.width(),
|
|
||||||
height=din_pin.height())
|
|
||||||
|
|
||||||
dout_pin = self.dff_insts[row,col].get_pin("Q")
|
|
||||||
debug.check(dout_pin.layer=="metal2","DFF Q pin not on metal2")
|
|
||||||
self.add_layout_pin(text=self.get_dout_name(row,col),
|
|
||||||
layer=dout_pin.layer,
|
|
||||||
offset=dout_pin.ll(),
|
|
||||||
width=dout_pin.width(),
|
|
||||||
height=dout_pin.height())
|
|
||||||
|
|
||||||
dout_bar_pin = self.dff_insts[row,col].get_pin("Qb")
|
|
||||||
debug.check(dout_bar_pin.layer=="metal2","DFF Qb pin not on metal2")
|
|
||||||
self.add_layout_pin(text=self.get_dout_bar_name(row,col),
|
|
||||||
layer=dout_bar_pin.layer,
|
|
||||||
offset=dout_bar_pin.ll(),
|
|
||||||
width=dout_bar_pin.width(),
|
|
||||||
height=dout_bar_pin.height())
|
|
||||||
|
|
||||||
|
|
||||||
# Create vertical spines to a single horizontal rail
|
|
||||||
clk_pin = self.dff_insts[0,0].get_pin("clk")
|
|
||||||
clk_ypos = 2*self.m3_pitch+self.m3_width
|
|
||||||
debug.check(clk_pin.layer=="metal2","DFF clk pin not on metal2")
|
|
||||||
if self.columns==1:
|
|
||||||
self.add_layout_pin(text="clk",
|
|
||||||
layer="metal2",
|
|
||||||
offset=clk_pin.ll().scale(1,0),
|
|
||||||
width=self.m2_width,
|
|
||||||
height=self.height)
|
|
||||||
else:
|
|
||||||
self.add_layout_pin_segment_center(text="clk",
|
|
||||||
layer="metal3",
|
|
||||||
start=vector(0,clk_ypos),
|
|
||||||
end=vector(self.width,clk_ypos))
|
|
||||||
for col in range(self.columns):
|
|
||||||
clk_pin = self.dff_insts[0,col].get_pin("clk")
|
|
||||||
# Make a vertical strip for each column
|
|
||||||
self.add_rect(layer="metal2",
|
|
||||||
offset=clk_pin.ll().scale(1,0),
|
|
||||||
width=self.m2_width,
|
|
||||||
height=self.height)
|
|
||||||
# Drop a via to the M3 pin
|
|
||||||
self.add_via_center(layers=("metal2","via2","metal3"),
|
|
||||||
offset=vector(clk_pin.cx(),clk_ypos))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def analytical_delay(self, slew, load=0.0):
|
|
||||||
return self.dff.analytical_delay(slew=slew, load=load)
|
|
||||||
|
|
@ -17,15 +17,11 @@ class hierarchical_decoder(design.design):
|
||||||
"""
|
"""
|
||||||
Dynamically generated hierarchical decoder.
|
Dynamically generated hierarchical decoder.
|
||||||
"""
|
"""
|
||||||
|
unique_id = 1
|
||||||
def __init__(self, rows):
|
|
||||||
design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows))
|
def __init__(self, rows, height=None):
|
||||||
|
design.design.__init__(self, "hierarchical_decoder_{0}rows_{1}".format(rows,hierarchical_decoder.unique_id))
|
||||||
from importlib import reload
|
hierarchical_decoder.unique_id += 1
|
||||||
c = reload(__import__(OPTS.bitcell))
|
|
||||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
|
||||||
b = self.mod_bitcell()
|
|
||||||
self.bitcell_height = b.height
|
|
||||||
|
|
||||||
self.NAND_FORMAT = "DEC_NAND_{0}"
|
self.NAND_FORMAT = "DEC_NAND_{0}"
|
||||||
self.INV_FORMAT = "DEC_INV_{0}"
|
self.INV_FORMAT = "DEC_INV_{0}"
|
||||||
|
|
@ -33,6 +29,7 @@ class hierarchical_decoder(design.design):
|
||||||
self.pre2x4_inst = []
|
self.pre2x4_inst = []
|
||||||
self.pre3x8_inst = []
|
self.pre3x8_inst = []
|
||||||
|
|
||||||
|
self.cell_height = height
|
||||||
self.rows = rows
|
self.rows = rows
|
||||||
self.num_inputs = int(math.log(self.rows, 2))
|
self.num_inputs = int(math.log(self.rows, 2))
|
||||||
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
(self.no_of_pre2x4,self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
|
||||||
|
|
@ -60,21 +57,21 @@ class hierarchical_decoder(design.design):
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
self.inv = pinv()
|
self.inv = pinv(height=self.cell_height)
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
self.nand2 = pnand2()
|
self.nand2 = pnand2(height=self.cell_height)
|
||||||
self.add_mod(self.nand2)
|
self.add_mod(self.nand2)
|
||||||
self.nand3 = pnand3()
|
self.nand3 = pnand3(height=self.cell_height)
|
||||||
self.add_mod(self.nand3)
|
self.add_mod(self.nand3)
|
||||||
|
|
||||||
self.add_decoders()
|
self.add_decoders()
|
||||||
|
|
||||||
def add_decoders(self):
|
def add_decoders(self):
|
||||||
""" Create the decoders based on the number of pre-decodes """
|
""" Create the decoders based on the number of pre-decodes """
|
||||||
self.pre2_4 = pre2x4()
|
self.pre2_4 = pre2x4(height=self.cell_height)
|
||||||
self.add_mod(self.pre2_4)
|
self.add_mod(self.pre2_4)
|
||||||
|
|
||||||
self.pre3_8 = pre3x8()
|
self.pre3_8 = pre3x8(height=self.cell_height)
|
||||||
self.add_mod(self.pre3_8)
|
self.add_mod(self.pre3_8)
|
||||||
|
|
||||||
def determine_predecodes(self,num_inputs):
|
def determine_predecodes(self,num_inputs):
|
||||||
|
|
@ -336,7 +333,7 @@ class hierarchical_decoder(design.design):
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for i in range(len(self.predec_groups[0])):
|
for i in range(len(self.predec_groups[0])):
|
||||||
for j in range(len(self.predec_groups[1])):
|
for j in range(len(self.predec_groups[1])):
|
||||||
row = len(self.predec_groups[1])*i + j
|
row = len(self.predec_groups[0])*j + i
|
||||||
name = self.NAND_FORMAT.format(row)
|
name = self.NAND_FORMAT.format(row)
|
||||||
self.nand_inst.append(self.add_inst(name=name,
|
self.nand_inst.append(self.add_inst(name=name,
|
||||||
mod=self.nand2))
|
mod=self.nand2))
|
||||||
|
|
@ -352,8 +349,8 @@ class hierarchical_decoder(design.design):
|
||||||
for i in range(len(self.predec_groups[0])):
|
for i in range(len(self.predec_groups[0])):
|
||||||
for j in range(len(self.predec_groups[1])):
|
for j in range(len(self.predec_groups[1])):
|
||||||
for k in range(len(self.predec_groups[2])):
|
for k in range(len(self.predec_groups[2])):
|
||||||
row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
|
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
|
||||||
+ len(self.predec_groups[2])*j + k
|
+ len(self.predec_groups[0])*j + i
|
||||||
|
|
||||||
name = self.NAND_FORMAT.format(row)
|
name = self.NAND_FORMAT.format(row)
|
||||||
self.nand_inst.append(self.add_inst(name=name,
|
self.nand_inst.append(self.add_inst(name=name,
|
||||||
|
|
@ -523,8 +520,8 @@ class hierarchical_decoder(design.design):
|
||||||
"""
|
"""
|
||||||
row_index = 0
|
row_index = 0
|
||||||
if (self.num_inputs == 4 or self.num_inputs == 5):
|
if (self.num_inputs == 4 or self.num_inputs == 5):
|
||||||
for index_A in self.predec_groups[0]:
|
for index_B in self.predec_groups[1]:
|
||||||
for index_B in self.predec_groups[1]:
|
for index_A in self.predec_groups[0]:
|
||||||
# FIXME: convert to connect_bus?
|
# FIXME: convert to connect_bus?
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||||
|
|
@ -533,9 +530,9 @@ class hierarchical_decoder(design.design):
|
||||||
row_index = row_index + 1
|
row_index = row_index + 1
|
||||||
|
|
||||||
elif (self.num_inputs > 5):
|
elif (self.num_inputs > 5):
|
||||||
for index_A in self.predec_groups[0]:
|
for index_C in self.predec_groups[2]:
|
||||||
for index_B in self.predec_groups[1]:
|
for index_B in self.predec_groups[1]:
|
||||||
for index_C in self.predec_groups[2]:
|
for index_A in self.predec_groups[0]:
|
||||||
# FIXME: convert to connect_bus?
|
# FIXME: convert to connect_bus?
|
||||||
predecode_name = "predecode_{}".format(index_A)
|
predecode_name = "predecode_{}".format(index_A)
|
||||||
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
self.route_predecode_rail(predecode_name, self.nand_inst[row_index].get_pin("A"))
|
||||||
|
|
|
||||||
|
|
@ -9,19 +9,18 @@ from globals import OPTS
|
||||||
from pnand2 import pnand2
|
from pnand2 import pnand2
|
||||||
from pnand3 import pnand3
|
from pnand3 import pnand3
|
||||||
|
|
||||||
|
|
||||||
class hierarchical_predecode(design.design):
|
class hierarchical_predecode(design.design):
|
||||||
"""
|
"""
|
||||||
Pre 2x4 and 3x8 decoder shared code.
|
Pre 2x4 and 3x8 decoder shared code.
|
||||||
"""
|
"""
|
||||||
def __init__(self, input_number):
|
unique_id = 1
|
||||||
|
|
||||||
|
def __init__(self, input_number, height=None):
|
||||||
self.number_of_inputs = input_number
|
self.number_of_inputs = input_number
|
||||||
|
self.cell_height = height
|
||||||
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
|
||||||
design.design.__init__(self, name="pre{0}x{1}".format(self.number_of_inputs,self.number_of_outputs))
|
design.design.__init__(self, name="pre{0}x{1}_{2}".format(self.number_of_inputs,self.number_of_outputs,hierarchical_predecode.unique_id))
|
||||||
|
hierarchical_predecode.unique_id += 1
|
||||||
from importlib import reload
|
|
||||||
c = reload(__import__(OPTS.bitcell))
|
|
||||||
self.mod_bitcell = getattr(c, OPTS.bitcell)
|
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
for k in range(self.number_of_inputs):
|
for k in range(self.number_of_inputs):
|
||||||
|
|
@ -34,7 +33,7 @@ class hierarchical_predecode(design.design):
|
||||||
def add_modules(self):
|
def add_modules(self):
|
||||||
""" Add the INV and NAND gate modules """
|
""" Add the INV and NAND gate modules """
|
||||||
|
|
||||||
self.inv = pinv()
|
self.inv = pinv(height=self.cell_height)
|
||||||
self.add_mod(self.inv)
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
self.add_nand(self.number_of_inputs)
|
self.add_nand(self.number_of_inputs)
|
||||||
|
|
@ -43,9 +42,9 @@ class hierarchical_predecode(design.design):
|
||||||
def add_nand(self,inputs):
|
def add_nand(self,inputs):
|
||||||
""" Create the NAND for the predecode input stage """
|
""" Create the NAND for the predecode input stage """
|
||||||
if inputs==2:
|
if inputs==2:
|
||||||
self.nand = pnand2()
|
self.nand = pnand2(height=self.cell_height)
|
||||||
elif inputs==3:
|
elif inputs==3:
|
||||||
self.nand = pnand3()
|
self.nand = pnand3(height=self.cell_height)
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
|
debug.error("Invalid number of predecode inputs: {}".format(inputs),-1)
|
||||||
|
|
||||||
|
|
@ -90,7 +89,7 @@ class hierarchical_predecode(design.design):
|
||||||
""" Create the input inverters to invert input signals for the decode stage. """
|
""" Create the input inverters to invert input signals for the decode stage. """
|
||||||
self.in_inst = []
|
self.in_inst = []
|
||||||
for inv_num in range(self.number_of_inputs):
|
for inv_num in range(self.number_of_inputs):
|
||||||
name = "Xpre_inv_{0}".format(inv_num)
|
name = "pre_inv_{0}".format(inv_num)
|
||||||
self.in_inst.append(self.add_inst(name=name,
|
self.in_inst.append(self.add_inst(name=name,
|
||||||
mod=self.inv))
|
mod=self.inv))
|
||||||
self.connect_inst(["in_{0}".format(inv_num),
|
self.connect_inst(["in_{0}".format(inv_num),
|
||||||
|
|
@ -114,7 +113,7 @@ class hierarchical_predecode(design.design):
|
||||||
""" Create inverters for the inverted output decode signals. """
|
""" Create inverters for the inverted output decode signals. """
|
||||||
self.inv_inst = []
|
self.inv_inst = []
|
||||||
for inv_num in range(self.number_of_outputs):
|
for inv_num in range(self.number_of_outputs):
|
||||||
name = "Xpre_nand_inv_{}".format(inv_num)
|
name = "pre_nand_inv_{}".format(inv_num)
|
||||||
self.inv_inst.append(self.add_inst(name=name,
|
self.inv_inst.append(self.add_inst(name=name,
|
||||||
mod=self.inv))
|
mod=self.inv))
|
||||||
self.connect_inst(["Z_{}".format(inv_num),
|
self.connect_inst(["Z_{}".format(inv_num),
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ class hierarchical_predecode2x4(hierarchical_predecode):
|
||||||
"""
|
"""
|
||||||
Pre 2x4 decoder used in hierarchical_decoder.
|
Pre 2x4 decoder used in hierarchical_decoder.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, height=None):
|
||||||
hierarchical_predecode.__init__(self, 2)
|
hierarchical_predecode.__init__(self, 2, height)
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ class hierarchical_predecode3x8(hierarchical_predecode):
|
||||||
"""
|
"""
|
||||||
Pre 3x8 decoder used in hierarchical_decoder.
|
Pre 3x8 decoder used in hierarchical_decoder.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, height=None):
|
||||||
hierarchical_predecode.__init__(self, 3)
|
hierarchical_predecode.__init__(self, 3, height)
|
||||||
|
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ class precharge_array(design.design):
|
||||||
for i in range(self.columns):
|
for i in range(self.columns):
|
||||||
self.add_pin("bl_{0}".format(i))
|
self.add_pin("bl_{0}".format(i))
|
||||||
self.add_pin("br_{0}".format(i))
|
self.add_pin("br_{0}".format(i))
|
||||||
self.add_pin("en")
|
self.add_pin("en_bar")
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
|
|
@ -59,9 +59,9 @@ class precharge_array(design.design):
|
||||||
|
|
||||||
def add_layout_pins(self):
|
def add_layout_pins(self):
|
||||||
|
|
||||||
self.add_layout_pin(text="en",
|
self.add_layout_pin(text="en_bar",
|
||||||
layer="metal1",
|
layer="metal1",
|
||||||
offset=self.pc_cell.get_pin("en").ll(),
|
offset=self.pc_cell.get_pin("en_bar").ll(),
|
||||||
width=self.width,
|
width=self.width,
|
||||||
height=drc("minwidth_metal1"))
|
height=drc("minwidth_metal1"))
|
||||||
|
|
||||||
|
|
@ -94,7 +94,7 @@ class precharge_array(design.design):
|
||||||
mod=self.pc_cell,
|
mod=self.pc_cell,
|
||||||
offset=offset)
|
offset=offset)
|
||||||
self.local_insts.append(inst)
|
self.local_insts.append(inst)
|
||||||
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en", "vdd"])
|
self.connect_inst(["bl_{0}".format(i), "br_{0}".format(i), "en_bar", "vdd"])
|
||||||
|
|
||||||
|
|
||||||
def place_insts(self):
|
def place_insts(self):
|
||||||
|
|
|
||||||
|
|
@ -265,15 +265,8 @@ class replica_bitline(design.design):
|
||||||
pin = self.rbl_inv_inst.get_pin("vdd")
|
pin = self.rbl_inv_inst.get_pin("vdd")
|
||||||
self.add_power_pin("vdd", pin.lc())
|
self.add_power_pin("vdd", pin.lc())
|
||||||
|
|
||||||
# Replica bitcell needs to be routed up to M3
|
|
||||||
pin=self.rbc_inst.get_pin("vdd")
|
pin=self.rbc_inst.get_pin("vdd")
|
||||||
# Don't rotate this via to vit in FreePDK45. In the custom cell, the pin cannot be placed
|
self.add_power_pin("vdd", pin.center(), 0, pin.layer)
|
||||||
# directly on vdd or there will be a drc error with a wordline. Place the pin slightly farther
|
|
||||||
# away then route to it. A better solution would be to rotate the m1 in the via or move the pin
|
|
||||||
# a m1_pitch below the entire cell.
|
|
||||||
pin_extension = pin.center() - vector(0,self.m1_pitch)
|
|
||||||
self.add_power_pin("vdd", pin_extension, rotate=0)
|
|
||||||
self.add_path("metal1", [pin.center(), pin_extension])
|
|
||||||
|
|
||||||
for pin in self.rbc_inst.get_pins("gnd"):
|
for pin in self.rbc_inst.get_pins("gnd"):
|
||||||
self.add_power_pin("gnd", pin.center())
|
self.add_power_pin("gnd", pin.center())
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class wordline_driver(design.design):
|
||||||
# Outputs from wordline_driver.
|
# Outputs from wordline_driver.
|
||||||
for i in range(self.rows):
|
for i in range(self.rows):
|
||||||
self.add_pin("wl_{0}".format(i))
|
self.add_pin("wl_{0}".format(i))
|
||||||
self.add_pin("en")
|
self.add_pin("en_bar")
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
|
|
@ -67,7 +67,7 @@ class wordline_driver(design.design):
|
||||||
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
|
||||||
|
|
||||||
# Find the x offsets for where the vias/pins should be placed
|
# Find the x offsets for where the vias/pins should be placed
|
||||||
a_xoffset = self.inv1_inst[0].rx()
|
a_xoffset = self.nand_inst[0].rx()
|
||||||
b_xoffset = self.inv2_inst[0].lx()
|
b_xoffset = self.inv2_inst[0].lx()
|
||||||
for num in range(self.rows):
|
for num in range(self.rows):
|
||||||
# this will result in duplicate polygons for rails, but who cares
|
# this will result in duplicate polygons for rails, but who cares
|
||||||
|
|
@ -95,24 +95,16 @@ class wordline_driver(design.design):
|
||||||
|
|
||||||
|
|
||||||
def create_drivers(self):
|
def create_drivers(self):
|
||||||
self.inv1_inst = []
|
|
||||||
self.nand_inst = []
|
self.nand_inst = []
|
||||||
self.inv2_inst = []
|
self.inv2_inst = []
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
name_inv1 = "wl_driver_inv_en{}".format(row)
|
|
||||||
name_nand = "wl_driver_nand{}".format(row)
|
name_nand = "wl_driver_nand{}".format(row)
|
||||||
name_inv2 = "wl_driver_inv{}".format(row)
|
name_inv2 = "wl_driver_inv{}".format(row)
|
||||||
|
|
||||||
# add inv1 based on the info above
|
|
||||||
self.inv1_inst.append(self.add_inst(name=name_inv1,
|
|
||||||
mod=self.inv_no_output))
|
|
||||||
self.connect_inst(["en",
|
|
||||||
"en_bar_{0}".format(row),
|
|
||||||
"vdd", "gnd"])
|
|
||||||
# add nand 2
|
# add nand 2
|
||||||
self.nand_inst.append(self.add_inst(name=name_nand,
|
self.nand_inst.append(self.add_inst(name=name_nand,
|
||||||
mod=self.nand2))
|
mod=self.nand2))
|
||||||
self.connect_inst(["en_bar_{0}".format(row),
|
self.connect_inst(["en_bar",
|
||||||
"in_{0}".format(row),
|
"in_{0}".format(row),
|
||||||
"wl_bar_{0}".format(row),
|
"wl_bar_{0}".format(row),
|
||||||
"vdd", "gnd"])
|
"vdd", "gnd"])
|
||||||
|
|
@ -125,8 +117,7 @@ class wordline_driver(design.design):
|
||||||
|
|
||||||
|
|
||||||
def place_drivers(self):
|
def place_drivers(self):
|
||||||
inv1_xoffset = 2*self.m1_width + 5*self.m1_space
|
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
|
||||||
nand2_xoffset = inv1_xoffset + self.inv.width
|
|
||||||
inv2_xoffset = nand2_xoffset + self.nand2.width
|
inv2_xoffset = nand2_xoffset + self.nand2.width
|
||||||
|
|
||||||
self.width = inv2_xoffset + self.inv.width
|
self.width = inv2_xoffset + self.inv.width
|
||||||
|
|
@ -140,13 +131,9 @@ class wordline_driver(design.design):
|
||||||
y_offset = self.inv.height*row
|
y_offset = self.inv.height*row
|
||||||
inst_mirror = "R0"
|
inst_mirror = "R0"
|
||||||
|
|
||||||
inv1_offset = [inv1_xoffset, y_offset]
|
|
||||||
nand2_offset=[nand2_xoffset, y_offset]
|
nand2_offset=[nand2_xoffset, y_offset]
|
||||||
inv2_offset=[inv2_xoffset, y_offset]
|
inv2_offset=[inv2_xoffset, y_offset]
|
||||||
|
|
||||||
# add inv1 based on the info above
|
|
||||||
self.inv1_inst[row].place(offset=inv1_offset,
|
|
||||||
mirror=inst_mirror)
|
|
||||||
# add nand 2
|
# add nand 2
|
||||||
self.nand_inst[row].place(offset=nand2_offset,
|
self.nand_inst[row].place(offset=nand2_offset,
|
||||||
mirror=inst_mirror)
|
mirror=inst_mirror)
|
||||||
|
|
@ -159,7 +146,7 @@ class wordline_driver(design.design):
|
||||||
""" Route all of the signals """
|
""" Route all of the signals """
|
||||||
|
|
||||||
# Wordline enable connection
|
# Wordline enable connection
|
||||||
en_pin=self.add_layout_pin(text="en",
|
en_pin=self.add_layout_pin(text="en_bar",
|
||||||
layer="metal2",
|
layer="metal2",
|
||||||
offset=[self.m1_width + 2*self.m1_space,0],
|
offset=[self.m1_width + 2*self.m1_space,0],
|
||||||
width=self.m2_width,
|
width=self.m2_width,
|
||||||
|
|
@ -167,12 +154,11 @@ class wordline_driver(design.design):
|
||||||
|
|
||||||
|
|
||||||
for row in range(self.rows):
|
for row in range(self.rows):
|
||||||
inv1_inst = self.inv1_inst[row]
|
|
||||||
nand_inst = self.nand_inst[row]
|
nand_inst = self.nand_inst[row]
|
||||||
inv2_inst = self.inv2_inst[row]
|
inv2_inst = self.inv2_inst[row]
|
||||||
|
|
||||||
# en connection
|
# en_bar connection
|
||||||
a_pin = inv1_inst.get_pin("A")
|
a_pin = nand_inst.get_pin("A")
|
||||||
a_pos = a_pin.lc()
|
a_pos = a_pin.lc()
|
||||||
clk_offset = vector(en_pin.bc().x,a_pos.y)
|
clk_offset = vector(en_pin.bc().x,a_pos.y)
|
||||||
self.add_segment_center(layer="metal1",
|
self.add_segment_center(layer="metal1",
|
||||||
|
|
@ -181,13 +167,6 @@ class wordline_driver(design.design):
|
||||||
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
self.add_via_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=clk_offset)
|
offset=clk_offset)
|
||||||
|
|
||||||
# first inv to nand2 A
|
|
||||||
zb_pos = inv1_inst.get_pin("Z").bc()
|
|
||||||
zu_pos = inv1_inst.get_pin("Z").uc()
|
|
||||||
bl_pos = nand_inst.get_pin("A").lc()
|
|
||||||
br_pos = nand_inst.get_pin("A").rc()
|
|
||||||
self.add_path("metal1", [zb_pos, zu_pos, bl_pos, br_pos])
|
|
||||||
|
|
||||||
# Nand2 out to 2nd inv
|
# Nand2 out to 2nd inv
|
||||||
zr_pos = nand_inst.get_pin("Z").rc()
|
zr_pos = nand_inst.get_pin("Z").rc()
|
||||||
al_pos = inv2_inst.get_pin("A").lc()
|
al_pos = inv2_inst.get_pin("A").lc()
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class write_driver_array(design.design):
|
||||||
def create_write_array(self):
|
def create_write_array(self):
|
||||||
self.driver_insts = {}
|
self.driver_insts = {}
|
||||||
for i in range(0,self.columns,self.words_per_row):
|
for i in range(0,self.columns,self.words_per_row):
|
||||||
name = "Xwrite_driver{}".format(i)
|
name = "write_driver{}".format(i)
|
||||||
index = int(i/self.words_per_row)
|
index = int(i/self.words_per_row)
|
||||||
self.driver_insts[index]=self.add_inst(name=name,
|
self.driver_insts[index]=self.add_inst(name=name,
|
||||||
mod=self.driver)
|
mod=self.driver)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,10 @@ if len(args) != 1:
|
||||||
# These depend on arguments, so don't load them until now.
|
# These depend on arguments, so don't load them until now.
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
|
# Keep track of running stats
|
||||||
|
start_time = datetime.datetime.now()
|
||||||
|
print_time("Start",start_time)
|
||||||
|
|
||||||
init_openram(config_file=args[0], is_unit_test=False)
|
init_openram(config_file=args[0], is_unit_test=False)
|
||||||
|
|
||||||
# Only print banner here so it's not in unit tests
|
# Only print banner here so it's not in unit tests
|
||||||
|
|
@ -34,10 +38,14 @@ print_banner()
|
||||||
# Output info about this run
|
# Output info about this run
|
||||||
report_status()
|
report_status()
|
||||||
|
|
||||||
# Start importing design modules after we have the config file
|
|
||||||
import verify
|
|
||||||
from sram import sram
|
|
||||||
from sram_config import sram_config
|
from sram_config import sram_config
|
||||||
|
|
||||||
|
|
||||||
|
# Configure the SRAM organization
|
||||||
|
c = sram_config(word_size=OPTS.word_size,
|
||||||
|
num_words=OPTS.num_words)
|
||||||
|
print("Words per row: {}".format(c.words_per_row))
|
||||||
|
|
||||||
#from parser import *
|
#from parser import *
|
||||||
output_extensions = ["sp","v","lib"]
|
output_extensions = ["sp","v","lib"]
|
||||||
if OPTS.datasheet_gen:
|
if OPTS.datasheet_gen:
|
||||||
|
|
@ -48,15 +56,8 @@ output_files = ["{0}.{1}".format(OPTS.output_name,x) for x in output_extensions]
|
||||||
print("Output files are: ")
|
print("Output files are: ")
|
||||||
print(*output_files,sep="\n")
|
print(*output_files,sep="\n")
|
||||||
|
|
||||||
# Keep track of running stats
|
|
||||||
start_time = datetime.datetime.now()
|
|
||||||
print_time("Start",start_time)
|
|
||||||
|
|
||||||
# Configure the SRAM organization
|
from sram import sram
|
||||||
c = sram_config(word_size=OPTS.word_size,
|
|
||||||
num_words=OPTS.num_words)
|
|
||||||
|
|
||||||
# import SRAM test generation
|
|
||||||
s = sram(sram_config=c,
|
s = sram(sram_config=c,
|
||||||
name=OPTS.output_name)
|
name=OPTS.output_name)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
import debug
|
||||||
|
from tech import drc
|
||||||
|
from math import log
|
||||||
|
from vector import vector
|
||||||
|
from globals import OPTS
|
||||||
|
from pnand2 import pnand2
|
||||||
|
from pinv import pinv
|
||||||
|
import pgate
|
||||||
|
|
||||||
|
class pand2(pgate.pgate):
|
||||||
|
"""
|
||||||
|
This is a simple buffer used for driving loads.
|
||||||
|
"""
|
||||||
|
from importlib import reload
|
||||||
|
c = reload(__import__(OPTS.bitcell))
|
||||||
|
bitcell = getattr(c, OPTS.bitcell)
|
||||||
|
|
||||||
|
unique_id = 1
|
||||||
|
|
||||||
|
def __init__(self, size=1, height=None, name=""):
|
||||||
|
|
||||||
|
self.size = size
|
||||||
|
|
||||||
|
if name=="":
|
||||||
|
name = "pand2_{0}_{1}".format(size, pand2.unique_id)
|
||||||
|
pand2.unique_id += 1
|
||||||
|
|
||||||
|
pgate.pgate.__init__(self, name, height)
|
||||||
|
debug.info(1, "Creating {}".format(self.name))
|
||||||
|
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
|
if not OPTS.netlist_only:
|
||||||
|
self.create_layout()
|
||||||
|
|
||||||
|
|
||||||
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
|
self.create_modules()
|
||||||
|
self.create_insts()
|
||||||
|
|
||||||
|
def create_modules(self):
|
||||||
|
# Shield the cap, but have at least a stage effort of 4
|
||||||
|
self.nand = pnand2(height=self.height)
|
||||||
|
self.add_mod(self.nand)
|
||||||
|
|
||||||
|
self.inv = pinv(size=self.size, height=self.height)
|
||||||
|
self.add_mod(self.inv)
|
||||||
|
|
||||||
|
def create_layout(self):
|
||||||
|
self.width = self.nand.width + self.inv.width
|
||||||
|
self.place_insts()
|
||||||
|
self.add_wires()
|
||||||
|
self.add_layout_pins()
|
||||||
|
|
||||||
|
def add_pins(self):
|
||||||
|
self.add_pin("A")
|
||||||
|
self.add_pin("B")
|
||||||
|
self.add_pin("Z")
|
||||||
|
self.add_pin("vdd")
|
||||||
|
self.add_pin("gnd")
|
||||||
|
|
||||||
|
def create_insts(self):
|
||||||
|
self.nand_inst=self.add_inst(name="pand2_nand",
|
||||||
|
mod=self.nand)
|
||||||
|
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
|
self.inv_inst=self.add_inst(name="pand2_inv",
|
||||||
|
mod=self.inv)
|
||||||
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_insts(self):
|
||||||
|
# Add NAND to the right
|
||||||
|
self.nand_inst.place(offset=vector(0,0))
|
||||||
|
|
||||||
|
# Add INV to the right
|
||||||
|
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0))
|
||||||
|
|
||||||
|
def add_wires(self):
|
||||||
|
# nand Z to inv A
|
||||||
|
z1_pin = self.nand_inst.get_pin("Z")
|
||||||
|
a2_pin = self.inv_inst.get_pin("A")
|
||||||
|
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
|
||||||
|
mid2_point = vector(mid1_point, a2_pin.cy())
|
||||||
|
self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
|
||||||
|
|
||||||
|
|
||||||
|
def add_layout_pins(self):
|
||||||
|
# Continous vdd rail along with label.
|
||||||
|
vdd_pin=self.inv_inst.get_pin("vdd")
|
||||||
|
self.add_layout_pin(text="vdd",
|
||||||
|
layer="metal1",
|
||||||
|
offset=vdd_pin.ll().scale(0,1),
|
||||||
|
width=self.width,
|
||||||
|
height=vdd_pin.height())
|
||||||
|
|
||||||
|
# Continous gnd rail along with label.
|
||||||
|
gnd_pin=self.inv_inst.get_pin("gnd")
|
||||||
|
self.add_layout_pin(text="gnd",
|
||||||
|
layer="metal1",
|
||||||
|
offset=gnd_pin.ll().scale(0,1),
|
||||||
|
width=self.width,
|
||||||
|
height=vdd_pin.height())
|
||||||
|
|
||||||
|
pin = self.inv_inst.get_pin("Z")
|
||||||
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
for pin_name in ["A","B"]:
|
||||||
|
pin = self.nand_inst.get_pin(pin_name)
|
||||||
|
self.add_layout_pin_rect_center(text=pin_name,
|
||||||
|
layer=pin.layer,
|
||||||
|
offset=pin.center(),
|
||||||
|
width=pin.width(),
|
||||||
|
height=pin.height())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def analytical_delay(self, slew, load=0.0):
|
||||||
|
""" Calculate the analytical delay of DFF-> INV -> INV """
|
||||||
|
nand_delay = selfnand.analytical_delay(slew=slew, load=self.inv.input_load())
|
||||||
|
inv_delay = self.inv.analytical_delay(slew=nand_delay.slew, load=load)
|
||||||
|
return nand_delay + inv_delay
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import debug
|
import debug
|
||||||
import design
|
|
||||||
from tech import drc
|
from tech import drc
|
||||||
from math import log
|
from math import log
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
from pinv import pinv
|
from pinv import pinv
|
||||||
|
import pgate
|
||||||
|
|
||||||
class pbuf(design.design):
|
class pbuf(pgate.pgate):
|
||||||
"""
|
"""
|
||||||
This is a simple buffer used for driving loads.
|
This is a simple buffer used for driving loads.
|
||||||
"""
|
"""
|
||||||
|
|
@ -16,39 +16,32 @@ class pbuf(design.design):
|
||||||
|
|
||||||
unique_id = 1
|
unique_id = 1
|
||||||
|
|
||||||
def __init__(self, driver_size=4, height=bitcell.height, name=""):
|
def __init__(self, size=4, height=None, name=""):
|
||||||
|
|
||||||
stage_effort = 4
|
self.stage_effort = 4
|
||||||
# FIXME: Change the number of stages to support high drives.
|
self.size = size
|
||||||
|
self.height = height
|
||||||
|
|
||||||
if name=="":
|
if name=="":
|
||||||
name = "pbuf_{0}_{1}".format(driver_size, pbuf.unique_id)
|
name = "pbuf_{0}_{1}".format(self.size, pbuf.unique_id)
|
||||||
pbuf.unique_id += 1
|
pbuf.unique_id += 1
|
||||||
|
|
||||||
design.design.__init__(self, name)
|
pgate.pgate.__init__(self, name, height)
|
||||||
debug.info(1, "Creating {}".format(self.name))
|
debug.info(1, "creating {0} with size of {1}".format(self.name,self.size))
|
||||||
|
|
||||||
|
self.create_netlist()
|
||||||
# Shield the cap, but have at least a stage effort of 4
|
if not OPTS.netlist_only:
|
||||||
input_size = max(1,int(driver_size/stage_effort))
|
self.create_layout()
|
||||||
self.inv1 = pinv(size=input_size, height=height) # 1
|
|
||||||
self.add_mod(self.inv1)
|
|
||||||
|
|
||||||
self.inv2 = pinv(size=driver_size, height=height) # 2
|
|
||||||
self.add_mod(self.inv2)
|
|
||||||
|
|
||||||
self.width = self.inv1.width + self.inv2.width
|
|
||||||
self.height = self.inv1.height
|
|
||||||
|
|
||||||
self.create_layout()
|
def create_netlist(self):
|
||||||
|
self.add_pins()
|
||||||
#self.offset_all_coordinates()
|
self.create_modules()
|
||||||
|
self.create_insts()
|
||||||
self.DRC_LVS()
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
self.add_pins()
|
self.width = self.inv1.width + self.inv2.width
|
||||||
self.add_insts()
|
self.place_insts()
|
||||||
self.add_wires()
|
self.add_wires()
|
||||||
self.add_layout_pins()
|
self.add_layout_pins()
|
||||||
|
|
||||||
|
|
@ -58,19 +51,31 @@ class pbuf(design.design):
|
||||||
self.add_pin("vdd")
|
self.add_pin("vdd")
|
||||||
self.add_pin("gnd")
|
self.add_pin("gnd")
|
||||||
|
|
||||||
def add_insts(self):
|
def create_modules(self):
|
||||||
# Add INV1 to the right
|
# Shield the cap, but have at least a stage effort of 4
|
||||||
|
input_size = max(1,int(self.size/self.stage_effort))
|
||||||
|
self.inv1 = pinv(size=input_size, height=self.height)
|
||||||
|
self.add_mod(self.inv1)
|
||||||
|
|
||||||
|
self.inv2 = pinv(size=self.size, height=self.height)
|
||||||
|
self.add_mod(self.inv2)
|
||||||
|
|
||||||
|
def create_insts(self):
|
||||||
self.inv1_inst=self.add_inst(name="buf_inv1",
|
self.inv1_inst=self.add_inst(name="buf_inv1",
|
||||||
mod=self.inv1,
|
mod=self.inv1)
|
||||||
offset=vector(0,0))
|
|
||||||
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
|
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
|
||||||
|
|
||||||
|
|
||||||
# Add INV2 to the right
|
|
||||||
self.inv2_inst=self.add_inst(name="buf_inv2",
|
self.inv2_inst=self.add_inst(name="buf_inv2",
|
||||||
mod=self.inv2,
|
mod=self.inv2)
|
||||||
offset=vector(self.inv1_inst.rx(),0))
|
|
||||||
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
|
||||||
|
|
||||||
|
def place_insts(self):
|
||||||
|
# Add INV1 to the right
|
||||||
|
self.inv1_inst.place(vector(0,0))
|
||||||
|
|
||||||
|
# Add INV2 to the right
|
||||||
|
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
|
||||||
|
|
||||||
|
|
||||||
def add_wires(self):
|
def add_wires(self):
|
||||||
|
|
@ -100,17 +105,17 @@ class pbuf(design.design):
|
||||||
|
|
||||||
z_pin = self.inv2_inst.get_pin("Z")
|
z_pin = self.inv2_inst.get_pin("Z")
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer="metal2",
|
layer=z_pin.layer,
|
||||||
offset=z_pin.center())
|
offset=z_pin.center(),
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
width=z_pin.width(),
|
||||||
offset=z_pin.center())
|
height=z_pin.height())
|
||||||
|
|
||||||
a_pin = self.inv1_inst.get_pin("A")
|
a_pin = self.inv1_inst.get_pin("A")
|
||||||
self.add_layout_pin_rect_center(text="A",
|
self.add_layout_pin_rect_center(text="A",
|
||||||
layer="metal2",
|
layer=a_pin.layer,
|
||||||
offset=a_pin.center())
|
offset=a_pin.center(),
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
width=a_pin.width(),
|
||||||
offset=a_pin.center())
|
height=a_pin.height())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ class pnand2(pgate.pgate):
|
||||||
self.create_netlist()
|
self.create_netlist()
|
||||||
if not OPTS.netlist_only:
|
if not OPTS.netlist_only:
|
||||||
self.create_layout()
|
self.create_layout()
|
||||||
#self.DRC_LVS()
|
|
||||||
|
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
|
|
@ -192,26 +191,33 @@ class pnand2(pgate.pgate):
|
||||||
""" Route the Z output """
|
""" Route the Z output """
|
||||||
# PMOS1 drain
|
# PMOS1 drain
|
||||||
pmos_pin = self.pmos1_inst.get_pin("D")
|
pmos_pin = self.pmos1_inst.get_pin("D")
|
||||||
|
top_pin_offset = pmos_pin.center()
|
||||||
# NMOS2 drain
|
# NMOS2 drain
|
||||||
nmos_pin = self.nmos2_inst.get_pin("D")
|
nmos_pin = self.nmos2_inst.get_pin("D")
|
||||||
|
bottom_pin_offset = nmos_pin.center()
|
||||||
|
|
||||||
# Output pin
|
# Output pin
|
||||||
mid_offset = vector(nmos_pin.center().x,self.inputA_yoffset)
|
out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset)
|
||||||
|
|
||||||
|
# Midpoints of the L routes go horizontal first then vertical
|
||||||
|
mid1_offset = vector(out_offset.x, top_pin_offset.y)
|
||||||
|
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
|
||||||
|
|
||||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=pmos_pin.center())
|
offset=pmos_pin.center())
|
||||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=nmos_pin.center())
|
offset=nmos_pin.center())
|
||||||
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
self.add_contact_center(layers=("metal1", "via1", "metal2"),
|
||||||
offset=mid_offset,
|
offset=out_offset,
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
# PMOS1 to mid-drain to NMOS2 drain
|
# PMOS1 to mid-drain to NMOS2 drain
|
||||||
self.add_path("metal2",[pmos_pin.bc(), mid_offset, nmos_pin.uc()])
|
self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])
|
||||||
|
|
||||||
# This extends the output to the edge of the cell
|
# This extends the output to the edge of the cell
|
||||||
self.add_layout_pin_rect_center(text="Z",
|
self.add_layout_pin_rect_center(text="Z",
|
||||||
layer="metal1",
|
layer="metal1",
|
||||||
offset=mid_offset,
|
offset=out_offset,
|
||||||
width=contact.m1m2.first_layer_height,
|
width=contact.m1m2.first_layer_height,
|
||||||
height=contact.m1m2.first_layer_width)
|
height=contact.m1m2.first_layer_width)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ class precharge(pgate.pgate):
|
||||||
self.DRC_LVS()
|
self.DRC_LVS()
|
||||||
|
|
||||||
def add_pins(self):
|
def add_pins(self):
|
||||||
self.add_pin_list(["bl", "br", "en", "vdd"])
|
self.add_pin_list(["bl", "br", "en_bar", "vdd"])
|
||||||
|
|
||||||
def add_ptx(self):
|
def add_ptx(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -92,15 +92,15 @@ class precharge(pgate.pgate):
|
||||||
|
|
||||||
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
|
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
|
||||||
mod=self.pmos)
|
mod=self.pmos)
|
||||||
self.connect_inst(["bl", "en", "br", "vdd"])
|
self.connect_inst(["bl", "en_bar", "br", "vdd"])
|
||||||
|
|
||||||
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
|
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
|
||||||
mod=self.pmos)
|
mod=self.pmos)
|
||||||
self.connect_inst(["bl", "en", "vdd", "vdd"])
|
self.connect_inst(["bl", "en_bar", "vdd", "vdd"])
|
||||||
|
|
||||||
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
|
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
|
||||||
mod=self.pmos)
|
mod=self.pmos)
|
||||||
self.connect_inst(["br", "en", "vdd", "vdd"])
|
self.connect_inst(["br", "en_bar", "vdd", "vdd"])
|
||||||
|
|
||||||
|
|
||||||
def place_ptx(self):
|
def place_ptx(self):
|
||||||
|
|
@ -161,7 +161,7 @@ class precharge(pgate.pgate):
|
||||||
rotate=90)
|
rotate=90)
|
||||||
|
|
||||||
# adds the en rail on metal1
|
# adds the en rail on metal1
|
||||||
self.add_layout_pin_segment_center(text="en",
|
self.add_layout_pin_segment_center(text="en_bar",
|
||||||
layer="metal1",
|
layer="metal1",
|
||||||
start=offset.scale(0,1),
|
start=offset.scale(0,1),
|
||||||
end=offset.scale(0,1)+vector(self.width,0))
|
end=offset.scale(0,1)+vector(self.width,0))
|
||||||
|
|
|
||||||
|
|
@ -68,12 +68,12 @@ class ptx(design.design):
|
||||||
# Just make a guess since these will actually be decided in the layout later.
|
# Just make a guess since these will actually be decided in the layout later.
|
||||||
area_sd = 2.5*drc("minwidth_poly")*self.tx_width
|
area_sd = 2.5*drc("minwidth_poly")*self.tx_width
|
||||||
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width
|
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width
|
||||||
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4}u ps={4}u as={5}p ad={5}p".format(spice[self.tx_type],
|
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type],
|
||||||
self.mults,
|
self.mults,
|
||||||
self.tx_width,
|
self.tx_width,
|
||||||
drc("minwidth_poly"),
|
drc("minwidth_poly"),
|
||||||
perimeter_sd,
|
perimeter_sd,
|
||||||
area_sd)
|
area_sd)
|
||||||
self.spice.append("\n* ptx " + self.spice_device)
|
self.spice.append("\n* ptx " + self.spice_device)
|
||||||
# self.spice.append(".ENDS {0}".format(self.name))
|
# self.spice.append(".ENDS {0}".format(self.name))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,6 @@ class pin_group:
|
||||||
ymax = max(plc.y,elc.y)
|
ymax = max(plc.y,elc.y)
|
||||||
ll = vector(plc.x, ymin)
|
ll = vector(plc.x, ymin)
|
||||||
ur = vector(prc.x, ymax)
|
ur = vector(prc.x, ymax)
|
||||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
|
||||||
elif pin.yoverlaps(enclosure):
|
elif pin.yoverlaps(enclosure):
|
||||||
# Is it horizontal overlap, extend pin shape to enclosure
|
# Is it horizontal overlap, extend pin shape to enclosure
|
||||||
pbc = pin.bc()
|
pbc = pin.bc()
|
||||||
|
|
@ -183,7 +182,6 @@ class pin_group:
|
||||||
xmax = max(pbc.x,ebc.x)
|
xmax = max(pbc.x,ebc.x)
|
||||||
ll = vector(xmin, pbc.y)
|
ll = vector(xmin, pbc.y)
|
||||||
ur = vector(xmax, puc.y)
|
ur = vector(xmax, puc.y)
|
||||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
|
||||||
else:
|
else:
|
||||||
# Neither, so we must do a corner-to corner
|
# Neither, so we must do a corner-to corner
|
||||||
pc = pin.center()
|
pc = pin.center()
|
||||||
|
|
@ -194,8 +192,10 @@ class pin_group:
|
||||||
ymax = max(pc.y, ec.y)
|
ymax = max(pc.y, ec.y)
|
||||||
ll = vector(xmin, ymin)
|
ll = vector(xmin, ymin)
|
||||||
ur = vector(xmax, ymax)
|
ur = vector(xmax, ymax)
|
||||||
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
|
||||||
|
|
||||||
|
if ll.x==ur.x or ll.y==ur.y:
|
||||||
|
return None
|
||||||
|
p = pin_layout(pin.name, [ll, ur], pin.layer)
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def find_above_connector(self, pin, enclosures):
|
def find_above_connector(self, pin, enclosures):
|
||||||
|
|
@ -226,7 +226,7 @@ class pin_group:
|
||||||
# If it already overlaps, no connector needed
|
# If it already overlaps, no connector needed
|
||||||
if above_item.overlaps(pin):
|
if above_item.overlaps(pin):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# Otherwise, make a connector to the item
|
# Otherwise, make a connector to the item
|
||||||
p = self.compute_connector(pin, above_item)
|
p = self.compute_connector(pin, above_item)
|
||||||
return p
|
return p
|
||||||
|
|
@ -485,7 +485,9 @@ class pin_group:
|
||||||
for pin_list in self.pins:
|
for pin_list in self.pins:
|
||||||
if not self.overlap_any_shape(pin_list, self.enclosures):
|
if not self.overlap_any_shape(pin_list, self.enclosures):
|
||||||
connector = self.find_smallest_connector(pin_list, self.enclosures)
|
connector = self.find_smallest_connector(pin_list, self.enclosures)
|
||||||
debug.check(connector!=None, "Could not find a connector for {} with {}".format(pin_list, self.enclosures))
|
if connector==None:
|
||||||
|
debug.error("Could not find a connector for {} with {}".format(pin_list, self.enclosures))
|
||||||
|
self.router.write_debug_gds("no_connector.gds")
|
||||||
self.enclosures.append(connector)
|
self.enclosures.append(connector)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -559,15 +561,13 @@ class pin_group:
|
||||||
of any grid in the other set.
|
of any grid in the other set.
|
||||||
"""
|
"""
|
||||||
# We could optimize this to just check the boundaries
|
# We could optimize this to just check the boundaries
|
||||||
g1_grids = set()
|
adj_grids = set()
|
||||||
g2_grids = set()
|
|
||||||
for g1 in self.grids:
|
for g1 in self.grids:
|
||||||
for g2 in other.grids:
|
for g2 in other.grids:
|
||||||
if g1.distance(g2) <= separation:
|
if g1.distance(g2) <= separation:
|
||||||
g1_grids.add(g1)
|
adj_grids.add(g1)
|
||||||
g2_grids.add(g2)
|
|
||||||
|
|
||||||
return g1_grids,g2_grids
|
return adj_grids
|
||||||
|
|
||||||
def convert_pin(self):
|
def convert_pin(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -576,14 +576,16 @@ class pin_group:
|
||||||
should be either blocked or part of the pin.
|
should be either blocked or part of the pin.
|
||||||
"""
|
"""
|
||||||
pin_set = set()
|
pin_set = set()
|
||||||
|
partial_set = set()
|
||||||
blockage_set = set()
|
blockage_set = set()
|
||||||
|
|
||||||
for pin_list in self.pins:
|
for pin_list in self.pins:
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
debug.info(2," Converting {0}".format(pin))
|
debug.info(2," Converting {0}".format(pin))
|
||||||
# Determine which tracks the pin overlaps
|
# Determine which tracks the pin overlaps
|
||||||
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin)
|
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
|
||||||
pin_set.update(pin_in_tracks)
|
pin_set.update(sufficient)
|
||||||
|
partial_set.update(insufficient)
|
||||||
|
|
||||||
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
# Blockages will be a super-set of pins since it uses the inflated pin shape.
|
||||||
blockage_in_tracks = self.router.convert_blockage(pin)
|
blockage_in_tracks = self.router.convert_blockage(pin)
|
||||||
|
|
@ -595,89 +597,94 @@ class pin_group:
|
||||||
if len(shared_set)>0:
|
if len(shared_set)>0:
|
||||||
debug.info(2,"Removing pins {}".format(shared_set))
|
debug.info(2,"Removing pins {}".format(shared_set))
|
||||||
pin_set.difference_update(shared_set)
|
pin_set.difference_update(shared_set)
|
||||||
|
shared_set = partial_set & self.router.blocked_grids
|
||||||
|
if len(shared_set)>0:
|
||||||
|
debug.info(2,"Removing pins {}".format(shared_set))
|
||||||
|
partial_set.difference_update(shared_set)
|
||||||
shared_set = blockage_set & self.router.blocked_grids
|
shared_set = blockage_set & self.router.blocked_grids
|
||||||
if len(shared_set)>0:
|
if len(shared_set)>0:
|
||||||
debug.info(2,"Removing blocks {}".format(shared_set))
|
debug.info(2,"Removing blocks {}".format(shared_set))
|
||||||
blockage_set.difference_update(shared_set)
|
blockage_set.difference_update(shared_set)
|
||||||
|
|
||||||
# At least one of the groups must have some valid tracks
|
# At least one of the groups must have some valid tracks
|
||||||
if (len(pin_set)==0 and len(blockage_set)==0):
|
if (len(pin_set)==0 and len(partial_set)==0 and len(blockage_set)==0):
|
||||||
#debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
#debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
|
||||||
|
|
||||||
for pin_list in self.pins:
|
for pin_list in self.pins:
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
debug.info(2," Converting {0}".format(pin))
|
debug.warning(" Expanding conversion {0}".format(pin))
|
||||||
# Determine which tracks the pin overlaps
|
# Determine which tracks the pin overlaps
|
||||||
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
|
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
|
||||||
pin_set.update(pin_in_tracks)
|
pin_set.update(sufficient)
|
||||||
|
partial_set.update(insufficient)
|
||||||
|
|
||||||
if len(pin_set)==0:
|
if len(pin_set)==0 and len(partial_set)==0:
|
||||||
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins))
|
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins))
|
||||||
self.router.write_debug_gds("blocked_pin.gds")
|
self.router.write_debug_gds("blocked_pin.gds")
|
||||||
|
|
||||||
# We need to route each of the components, so don't combine the groups
|
# Consider all the grids that would be blocked
|
||||||
self.grids = pin_set | blockage_set
|
self.grids = pin_set | partial_set
|
||||||
# Remember the secondary grids for removing adjacent pins in wide metal spacing
|
# Remember the secondary grids for removing adjacent pins
|
||||||
self.secondary_grids = blockage_set - pin_set
|
self.secondary_grids = partial_set
|
||||||
|
|
||||||
debug.info(2," pins {}".format(self.grids))
|
debug.info(2," pins {}".format(self.grids))
|
||||||
debug.info(2," secondary {}".format(self.secondary_grids))
|
debug.info(2," secondary {}".format(self.secondary_grids))
|
||||||
|
|
||||||
def recurse_simple_overlap_enclosure(self, start_set, direct):
|
# def recurse_simple_overlap_enclosure(self, start_set, direct):
|
||||||
"""
|
# """
|
||||||
Recursive function to return set of tracks that connects to
|
# Recursive function to return set of tracks that connects to
|
||||||
the actual supply rail wire in a given direction (or terminating
|
# the actual supply rail wire in a given direction (or terminating
|
||||||
when any track is no longer in the supply rail.
|
# when any track is no longer in the supply rail.
|
||||||
"""
|
# """
|
||||||
next_set = grid_utils.expand_border(start_set, direct)
|
# next_set = grid_utils.expand_border(start_set, direct)
|
||||||
|
|
||||||
supply_tracks = self.router.supply_rail_tracks[self.name]
|
# supply_tracks = self.router.supply_rail_tracks[self.name]
|
||||||
supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
|
# supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
|
||||||
|
|
||||||
supply_overlap = next_set & supply_tracks
|
# supply_overlap = next_set & supply_tracks
|
||||||
wire_overlap = next_set & supply_wire_tracks
|
# wire_overlap = next_set & supply_wire_tracks
|
||||||
|
|
||||||
# If the rail overlap is the same, we are done, since we connected to the actual wire
|
# # If the rail overlap is the same, we are done, since we connected to the actual wire
|
||||||
if len(wire_overlap)==len(start_set):
|
# if len(wire_overlap)==len(start_set):
|
||||||
new_set = start_set | wire_overlap
|
# new_set = start_set | wire_overlap
|
||||||
# If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
|
# # If the supply overlap is the same, keep expanding unti we hit the wire or move out of the rail region
|
||||||
elif len(supply_overlap)==len(start_set):
|
# elif len(supply_overlap)==len(start_set):
|
||||||
recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
|
# recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
|
||||||
new_set = start_set | supply_overlap | recurse_set
|
# new_set = start_set | supply_overlap | recurse_set
|
||||||
else:
|
# else:
|
||||||
# If we got no next set, we are done, can't expand!
|
# # If we got no next set, we are done, can't expand!
|
||||||
new_set = set()
|
# new_set = set()
|
||||||
|
|
||||||
return new_set
|
# return new_set
|
||||||
|
|
||||||
def create_simple_overlap_enclosure(self, start_set):
|
# def create_simple_overlap_enclosure(self, start_set):
|
||||||
"""
|
# """
|
||||||
This takes a set of tracks that overlap a supply rail and creates an enclosure
|
# This takes a set of tracks that overlap a supply rail and creates an enclosure
|
||||||
that is ensured to overlap the supply rail wire.
|
# that is ensured to overlap the supply rail wire.
|
||||||
It then adds rectangle(s) for the enclosure.
|
# It then adds rectangle(s) for the enclosure.
|
||||||
"""
|
# """
|
||||||
additional_set = set()
|
# additional_set = set()
|
||||||
# Check the layer of any element in the pin to determine which direction to route it
|
# # Check the layer of any element in the pin to determine which direction to route it
|
||||||
e = next(iter(start_set))
|
# e = next(iter(start_set))
|
||||||
new_set = start_set.copy()
|
# new_set = start_set.copy()
|
||||||
if e.z==0:
|
# if e.z==0:
|
||||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
|
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
|
||||||
if not new_set:
|
# if not new_set:
|
||||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
|
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
|
||||||
else:
|
# else:
|
||||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
|
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
|
||||||
if not new_set:
|
# if not new_set:
|
||||||
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
|
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
|
||||||
|
|
||||||
# Expand the pin grid set to include some extra grids that connect the supply rail
|
# # Expand the pin grid set to include some extra grids that connect the supply rail
|
||||||
self.grids.update(new_set)
|
# self.grids.update(new_set)
|
||||||
|
|
||||||
# Add the inflated set so we don't get wide metal spacing issues (if it exists)
|
# # Block the grids
|
||||||
self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width))
|
# self.blockages.update(new_set)
|
||||||
|
|
||||||
# Add the polygon enclosures and set this pin group as routed
|
# # Add the polygon enclosures and set this pin group as routed
|
||||||
self.set_routed()
|
# self.set_routed()
|
||||||
self.enclosures = self.compute_enclosures()
|
# self.enclosures = self.compute_enclosures()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,12 @@ class router(router_tech):
|
||||||
It populates blockages on a grid class.
|
It populates blockages on a grid class.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, layers, design, gds_filename=None):
|
def __init__(self, layers, design, gds_filename=None, rail_track_width=1):
|
||||||
"""
|
"""
|
||||||
This will instantiate a copy of the gds file or the module at (0,0) and
|
This will instantiate a copy of the gds file or the module at (0,0) and
|
||||||
route on top of this. The blockages from the gds/module will be considered.
|
route on top of this. The blockages from the gds/module will be considered.
|
||||||
"""
|
"""
|
||||||
router_tech.__init__(self, layers)
|
router_tech.__init__(self, layers, rail_track_width)
|
||||||
|
|
||||||
self.cell = design
|
self.cell = design
|
||||||
|
|
||||||
|
|
@ -70,8 +70,7 @@ class router(router_tech):
|
||||||
self.boundary = self.layout.measureBoundary(self.top_name)
|
self.boundary = self.layout.measureBoundary(self.top_name)
|
||||||
# These must be un-indexed to get rid of the matrix type
|
# These must be un-indexed to get rid of the matrix type
|
||||||
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
|
self.ll = vector(self.boundary[0][0], self.boundary[0][1])
|
||||||
# Pad the UR by a few tracks to add an extra rail possibly
|
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
|
||||||
self.ur = vector(self.boundary[1][0], self.boundary[1][1]) + self.track_widths.scale(5,5)
|
|
||||||
|
|
||||||
def clear_pins(self):
|
def clear_pins(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -130,12 +129,8 @@ class router(router_tech):
|
||||||
Pin can either be a label or a location,layer pair: [[x,y],layer].
|
Pin can either be a label or a location,layer pair: [[x,y],layer].
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Finding pins for {}.".format(pin_name))
|
debug.info(1,"Finding pins for {}.".format(pin_name))
|
||||||
#start_time = datetime.now()
|
|
||||||
self.retrieve_pins(pin_name)
|
self.retrieve_pins(pin_name)
|
||||||
#print_time("Retrieved pins",datetime.now(), start_time)
|
|
||||||
#start_time = datetime.now()
|
|
||||||
self.analyze_pins(pin_name)
|
self.analyze_pins(pin_name)
|
||||||
#print_time("Analyzed pins",datetime.now(), start_time)
|
|
||||||
|
|
||||||
def find_blockages(self):
|
def find_blockages(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -160,48 +155,32 @@ class router(router_tech):
|
||||||
|
|
||||||
# This will get all shapes as blockages and convert to grid units
|
# This will get all shapes as blockages and convert to grid units
|
||||||
# This ignores shapes that were pins
|
# This ignores shapes that were pins
|
||||||
#start_time = datetime.now()
|
|
||||||
self.find_blockages()
|
self.find_blockages()
|
||||||
#print_time("Find blockags",datetime.now(), start_time)
|
|
||||||
|
|
||||||
# Convert the blockages to grid units
|
# Convert the blockages to grid units
|
||||||
#start_time = datetime.now()
|
|
||||||
self.convert_blockages()
|
self.convert_blockages()
|
||||||
#print_time("Find blockags",datetime.now(), start_time)
|
|
||||||
|
|
||||||
# This will convert the pins to grid units
|
# This will convert the pins to grid units
|
||||||
# It must be done after blockages to ensure no DRCs between expanded pins and blocked grids
|
# It must be done after blockages to ensure no DRCs between expanded pins and blocked grids
|
||||||
#start_time = datetime.now()
|
|
||||||
for pin in pin_list:
|
for pin in pin_list:
|
||||||
self.convert_pins(pin)
|
self.convert_pins(pin)
|
||||||
#print_time("Convert pins",datetime.now(), start_time)
|
|
||||||
|
|
||||||
#start_time = datetime.now()
|
#for pin in pin_list:
|
||||||
for pin in pin_list:
|
# self.combine_adjacent_pins(pin)
|
||||||
self.combine_adjacent_pins(pin)
|
|
||||||
#print_time("Combine pins",datetime.now(), start_time)
|
|
||||||
#self.write_debug_gds("debug_combine_pins.gds",stop_program=True)
|
|
||||||
|
|
||||||
# Separate any adjacent grids of differing net names to prevent wide metal DRC violations
|
# Separate any adjacent grids of differing net names that overlap
|
||||||
# Must be done before enclosing pins
|
# Must be done before enclosing pins
|
||||||
#start_time = datetime.now()
|
self.separate_adjacent_pins(0)
|
||||||
self.separate_adjacent_pins(self.supply_rail_space_width)
|
|
||||||
#print_time("Separate pins",datetime.now(), start_time)
|
|
||||||
# For debug
|
|
||||||
#self.separate_adjacent_pins(1)
|
|
||||||
|
|
||||||
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
|
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
|
||||||
#start_time = datetime.now()
|
|
||||||
self.enclose_pins()
|
self.enclose_pins()
|
||||||
#print_time("Enclose pins",datetime.now(), start_time)
|
|
||||||
#self.write_debug_gds("debug_enclose_pins.gds",stop_program=True)
|
|
||||||
|
|
||||||
|
|
||||||
def combine_adjacent_pins(self, pin_name):
|
def combine_adjacent_pins(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Find pins that have adjacent routing tracks and merge them into a
|
Find pins that have adjacent routing tracks and merge them into a
|
||||||
single pin_group. The pins themselves may not be touching, but
|
single pin_group. The pins themselves may not be touching, but
|
||||||
enclose_pis in the next step will ensure they are touching.
|
enclose_pins in the next step will ensure they are touching.
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
|
debug.info(1,"Combining adjacent pins for {}.".format(pin_name))
|
||||||
# Find all adjacencies
|
# Find all adjacencies
|
||||||
|
|
@ -257,18 +236,19 @@ class router(router_tech):
|
||||||
This will try to separate all grid pins by the supplied number of separation
|
This will try to separate all grid pins by the supplied number of separation
|
||||||
tracks (default is to prevent adjacency).
|
tracks (default is to prevent adjacency).
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Separating adjacent pins.")
|
|
||||||
# Commented out to debug with SCMOS
|
# Commented out to debug with SCMOS
|
||||||
#if separation==0:
|
#if separation==0:
|
||||||
# return
|
# return
|
||||||
|
|
||||||
pin_names = self.pin_groups.keys()
|
|
||||||
for pin_name1 in pin_names:
|
|
||||||
for pin_name2 in pin_names:
|
|
||||||
if pin_name1==pin_name2:
|
|
||||||
continue
|
|
||||||
self.separate_adjacent_pin(pin_name1, pin_name2, separation)
|
|
||||||
|
|
||||||
|
pin_names = self.pin_groups.keys()
|
||||||
|
for i,pin_name1 in enumerate(pin_names):
|
||||||
|
for j,pin_name2 in enumerate(pin_names):
|
||||||
|
if i==j:
|
||||||
|
continue
|
||||||
|
if i>j:
|
||||||
|
return
|
||||||
|
self.separate_adjacent_pin(pin_name1, pin_name2, separation)
|
||||||
|
|
||||||
def separate_adjacent_pin(self, pin_name1, pin_name2, separation):
|
def separate_adjacent_pin(self, pin_name1, pin_name2, separation):
|
||||||
"""
|
"""
|
||||||
Go through all of the pin groups and check if any other pin group is
|
Go through all of the pin groups and check if any other pin group is
|
||||||
|
|
@ -277,50 +257,57 @@ class router(router_tech):
|
||||||
Try to do this intelligently to keep th pins enclosed.
|
Try to do this intelligently to keep th pins enclosed.
|
||||||
"""
|
"""
|
||||||
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2))
|
debug.info(1,"Comparing {0} and {1} adjacency".format(pin_name1, pin_name2))
|
||||||
|
removed_grids = 0
|
||||||
for index1,pg1 in enumerate(self.pin_groups[pin_name1]):
|
for index1,pg1 in enumerate(self.pin_groups[pin_name1]):
|
||||||
for index2,pg2 in enumerate(self.pin_groups[pin_name2]):
|
for index2,pg2 in enumerate(self.pin_groups[pin_name2]):
|
||||||
# FIgXME: Use separation distance and edge grids only
|
adj_grids = pg1.adjacent_grids(pg2, separation)
|
||||||
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
|
removed_grids += len(adj_grids)
|
||||||
# These should have the same length, so...
|
# These should have the same length, so...
|
||||||
if len(grids_g1)>0:
|
if len(adj_grids)>0:
|
||||||
debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2))
|
debug.info(3,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids))
|
||||||
self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2)
|
self.remove_adjacent_grid(pg1, pg2, adj_grids)
|
||||||
|
|
||||||
|
|
||||||
def remove_adjacent_grid(self, pg1, grids1, pg2, grids2):
|
debug.info(1,"Removed {} adjacent grids.".format(removed_grids))
|
||||||
|
|
||||||
|
def remove_adjacent_grid(self, pg1, pg2, adj_grids):
|
||||||
"""
|
"""
|
||||||
Remove one of the adjacent grids in a heuristic manner.
|
Remove one of the adjacent grids in a heuristic manner.
|
||||||
|
This will try to keep the groups similar sized by removing from the bigger group.
|
||||||
"""
|
"""
|
||||||
# Determine the bigger and smaller group
|
|
||||||
if pg1.size()>pg2.size():
|
if pg1.size()>pg2.size():
|
||||||
bigger = pg1
|
bigger = pg1
|
||||||
bigger_grids = grids1
|
|
||||||
smaller = pg2
|
smaller = pg2
|
||||||
smaller_grids = grids2
|
|
||||||
else:
|
else:
|
||||||
bigger = pg2
|
bigger = pg2
|
||||||
bigger_grids = grids2
|
|
||||||
smaller = pg1
|
smaller = pg1
|
||||||
smaller_grids = grids1
|
|
||||||
|
|
||||||
# First, see if we can remove grids that are in the secondary grids
|
for adj in adj_grids:
|
||||||
# i.e. they aren't necessary to the pin grids
|
|
||||||
if bigger_grids.issubset(bigger.secondary_grids):
|
|
||||||
debug.info(3,"Removing {} from bigger {}".format(str(bigger_grids), bigger))
|
|
||||||
bigger.grids.difference_update(bigger_grids)
|
|
||||||
self.blocked_grids.update(bigger_grids)
|
|
||||||
return
|
|
||||||
elif smaller_grids.issubset(smaller.secondary_grids):
|
|
||||||
debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller))
|
|
||||||
smaller.grids.difference_update(smaller_grids)
|
|
||||||
self.blocked_grids.update(smaller_grids)
|
|
||||||
return
|
|
||||||
|
|
||||||
# If that fails, just randomly remove from the bigger one and give a warning.
|
|
||||||
# This might fail later.
|
# If the adjacent grids are a subset of the secondary grids (i.e. not necessary)
|
||||||
debug.warning("Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids))
|
# remove them from each
|
||||||
debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids))
|
if adj in bigger.secondary_grids:
|
||||||
bigger.grids.difference_update(bigger_grids)
|
debug.info(3,"Removing {} from bigger secondary {}".format(adj, bigger))
|
||||||
self.blocked_grids.update(bigger_grids)
|
bigger.grids.remove(adj)
|
||||||
|
bigger.secondary_grids.remove(adj)
|
||||||
|
self.blocked_grids.add(adj)
|
||||||
|
elif adj in smaller.secondary_grids:
|
||||||
|
debug.info(3,"Removing {} from smaller secondary {}".format(adj, smaller))
|
||||||
|
smaller.grids.remove(adj)
|
||||||
|
smaller.secondary_grids.remove(adj)
|
||||||
|
self.blocked_grids.add(adj)
|
||||||
|
else:
|
||||||
|
# If we couldn't remove from a secondary grid, we must remove from the primary
|
||||||
|
# grid of at least one pin
|
||||||
|
if adj in bigger.grids:
|
||||||
|
debug.info(3,"Removing {} from bigger primary {}".format(adj, bigger))
|
||||||
|
bigger.grids.remove(adj)
|
||||||
|
elif adj in smaller.grids:
|
||||||
|
debug.info(3,"Removing {} from smaller primary {}".format(adj, smaller))
|
||||||
|
smaller.grids.remove(adj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -359,17 +346,6 @@ class router(router_tech):
|
||||||
self.set_blockages(blockage_grids,False)
|
self.set_blockages(blockage_grids,False)
|
||||||
|
|
||||||
|
|
||||||
# def translate_coordinates(self, coord, mirr, angle, xyShift):
|
|
||||||
# """
|
|
||||||
# Calculate coordinates after flip, rotate, and shift
|
|
||||||
# """
|
|
||||||
# coordinate = []
|
|
||||||
# for item in coord:
|
|
||||||
# x = (item[0]*math.cos(angle)-item[1]*mirr*math.sin(angle)+xyShift[0])
|
|
||||||
# y = (item[0]*math.sin(angle)+item[1]*mirr*math.cos(angle)+xyShift[1])
|
|
||||||
# coordinate += [(x, y)]
|
|
||||||
# return coordinate
|
|
||||||
|
|
||||||
def convert_shape_to_units(self, shape):
|
def convert_shape_to_units(self, shape):
|
||||||
"""
|
"""
|
||||||
Scale a shape (two vector list) to user units
|
Scale a shape (two vector list) to user units
|
||||||
|
|
@ -496,11 +472,6 @@ class router(router_tech):
|
||||||
# and the track points are at the center
|
# and the track points are at the center
|
||||||
ll = ll.round()
|
ll = ll.round()
|
||||||
ur = ur.round()
|
ur = ur.round()
|
||||||
# if ll[0]<45 and ll[0]>35 and ll[1]<5 and ll[1]>-5:
|
|
||||||
# debug.info(0,"Converting [ {0} , {1} ]".format(old_ll,old_ur))
|
|
||||||
# debug.info(0,"Converted [ {0} , {1} ]".format(ll,ur))
|
|
||||||
# pin=self.convert_track_to_shape(ll)
|
|
||||||
# debug.info(0,"Pin {}".format(pin))
|
|
||||||
return [ll,ur]
|
return [ll,ur]
|
||||||
|
|
||||||
def convert_pin_to_tracks(self, pin_name, pin, expansion=0):
|
def convert_pin_to_tracks(self, pin_name, pin, expansion=0):
|
||||||
|
|
@ -515,36 +486,24 @@ class router(router_tech):
|
||||||
# scale the size bigger to include neaby tracks
|
# scale the size bigger to include neaby tracks
|
||||||
ll=ll.scale(self.track_factor).floor()
|
ll=ll.scale(self.track_factor).floor()
|
||||||
ur=ur.scale(self.track_factor).ceil()
|
ur=ur.scale(self.track_factor).ceil()
|
||||||
#print(pin)
|
|
||||||
# Keep tabs on tracks with sufficient and insufficient overlap
|
# Keep tabs on tracks with sufficient and insufficient overlap
|
||||||
sufficient_list = set()
|
sufficient_list = set()
|
||||||
insufficient_list = set()
|
insufficient_list = set()
|
||||||
|
|
||||||
zindex=self.get_zindex(pin.layer_num)
|
zindex=self.get_zindex(pin.layer_num)
|
||||||
for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion):
|
for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion):
|
||||||
for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion):
|
for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion):
|
||||||
(full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
|
(full_overlap, partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
|
||||||
if full_overlap:
|
if full_overlap:
|
||||||
sufficient_list.update([full_overlap])
|
sufficient_list.update([full_overlap])
|
||||||
if partial_overlap:
|
if partial_overlap:
|
||||||
insufficient_list.update([partial_overlap])
|
insufficient_list.update([partial_overlap])
|
||||||
debug.info(4,"Converting [ {0} , {1} ] full={2} partial={3}".format(x,y, full_overlap, partial_overlap))
|
debug.info(2,"Converting [ {0} , {1} ] full={2}".format(x,y, full_overlap))
|
||||||
|
|
||||||
# Remove the blocked grids
|
# Return all grids with any potential overlap (sufficient or not)
|
||||||
sufficient_list.difference_update(self.blocked_grids)
|
return (sufficient_list,insufficient_list)
|
||||||
insufficient_list.difference_update(self.blocked_grids)
|
|
||||||
|
|
||||||
if len(sufficient_list)>0:
|
|
||||||
return sufficient_list
|
|
||||||
elif expansion==0 and len(insufficient_list)>0:
|
|
||||||
best_pin = self.get_all_offgrid_pin(pin, insufficient_list)
|
|
||||||
#print(best_pin)
|
|
||||||
return best_pin
|
|
||||||
elif expansion>0:
|
|
||||||
nearest_pin = self.get_furthest_offgrid_pin(pin, insufficient_list)
|
|
||||||
return nearest_pin
|
|
||||||
else:
|
|
||||||
return set()
|
|
||||||
|
|
||||||
def get_all_offgrid_pin(self, pin, insufficient_list):
|
def get_all_offgrid_pin(self, pin, insufficient_list):
|
||||||
"""
|
"""
|
||||||
|
|
@ -568,7 +527,6 @@ class router(router_tech):
|
||||||
"""
|
"""
|
||||||
Find a list of the single pin with the most overlap.
|
Find a list of the single pin with the most overlap.
|
||||||
"""
|
"""
|
||||||
#print("INSUFFICIENT LIST",insufficient_list)
|
|
||||||
# Find the coordinate with the most overlap
|
# Find the coordinate with the most overlap
|
||||||
best_coord = None
|
best_coord = None
|
||||||
best_overlap = -math.inf
|
best_overlap = -math.inf
|
||||||
|
|
@ -589,7 +547,6 @@ class router(router_tech):
|
||||||
Get a grid cell that is the furthest from the blocked grids.
|
Get a grid cell that is the furthest from the blocked grids.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#print("INSUFFICIENT LIST",insufficient_list)
|
|
||||||
# Find the coordinate with the most overlap
|
# Find the coordinate with the most overlap
|
||||||
best_coord = None
|
best_coord = None
|
||||||
best_dist = math.inf
|
best_dist = math.inf
|
||||||
|
|
@ -606,7 +563,6 @@ class router(router_tech):
|
||||||
Given a pin and a list of grid cells (probably non-overlapping),
|
Given a pin and a list of grid cells (probably non-overlapping),
|
||||||
return the nearest grid cell (center to center).
|
return the nearest grid cell (center to center).
|
||||||
"""
|
"""
|
||||||
#print("INSUFFICIENT LIST",insufficient_list)
|
|
||||||
# Find the coordinate with the most overlap
|
# Find the coordinate with the most overlap
|
||||||
best_coord = None
|
best_coord = None
|
||||||
best_dist = math.inf
|
best_dist = math.inf
|
||||||
|
|
@ -622,30 +578,32 @@ class router(router_tech):
|
||||||
|
|
||||||
def convert_pin_coord_to_tracks(self, pin, coord):
|
def convert_pin_coord_to_tracks(self, pin, coord):
|
||||||
"""
|
"""
|
||||||
Given a pin and a track coordinate, determine if the pin overlaps enough.
|
Return all tracks that an inflated pin overlaps
|
||||||
If it does, add additional metal to make the pin "on grid".
|
|
||||||
If it doesn't, add it to the blocked grid list.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
(width, spacing) = self.get_layer_width_space(coord.z)
|
# This is using the full track shape rather than a single track pin shape
|
||||||
|
# because we will later patch a connector if there isn't overlap.
|
||||||
# This is the rectangle if we put a pin in the center of the track
|
track_pin = self.convert_track_to_shape_pin(coord)
|
||||||
track_pin = self.convert_track_to_pin(coord)
|
|
||||||
|
# This is the normal pin inflated by a minimum design rule
|
||||||
|
inflated_pin = pin_layout(pin.name, pin.inflate(0.5*self.track_space), pin.layer)
|
||||||
|
|
||||||
overlap_length = pin.overlap_length(track_pin)
|
overlap_length = pin.overlap_length(track_pin)
|
||||||
|
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
|
||||||
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
|
inflated_overlap_length = inflated_pin.overlap_length(track_pin)
|
||||||
# If it overlaps by more than the min width DRC, we can just use the track
|
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, inflated_overlap_length))
|
||||||
if overlap_length==math.inf or snap_val_to_grid(overlap_length) >= snap_val_to_grid(width):
|
|
||||||
debug.info(3," Overlap: {0} >? {1}".format(overlap_length,spacing))
|
# If it overlaps with the pin, it is sufficient
|
||||||
return (coord, None)
|
if overlap_length==math.inf or overlap_length > 0:
|
||||||
# Otherwise, keep track of the partial overlap grids in case we need to patch it later.
|
debug.info(2," Overlap: {0} >? {1}".format(overlap_length,0))
|
||||||
|
return (coord,None)
|
||||||
|
# If it overlaps with the inflated pin, it is partial
|
||||||
|
elif inflated_overlap_length==math.inf or inflated_overlap_length > 0:
|
||||||
|
debug.info(2," Partial overlap: {0} >? {1}".format(inflated_overlap_length,0))
|
||||||
|
return (None,coord)
|
||||||
else:
|
else:
|
||||||
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing))
|
debug.info(2," No overlap: {0} {1}".format(overlap_length,0))
|
||||||
return (None, coord)
|
return (None,None)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def convert_track_to_pin(self, track):
|
def convert_track_to_pin(self, track):
|
||||||
|
|
@ -653,25 +611,34 @@ class router(router_tech):
|
||||||
Convert a grid point into a rectangle shape that is centered
|
Convert a grid point into a rectangle shape that is centered
|
||||||
track in the track and leaves half a DRC space in each direction.
|
track in the track and leaves half a DRC space in each direction.
|
||||||
"""
|
"""
|
||||||
# space depends on which layer it is
|
|
||||||
if self.get_layer(track[2])==self.horiz_layer_name:
|
|
||||||
space = 0.5*self.horiz_layer_spacing
|
|
||||||
else:
|
|
||||||
space = 0.5*self.vert_layer_spacing
|
|
||||||
|
|
||||||
# calculate lower left
|
# calculate lower left
|
||||||
x = track.x*self.track_width - 0.5*self.track_width + space
|
x = track.x*self.track_width - 0.5*self.track_width + 0.5*self.track_space
|
||||||
y = track.y*self.track_width - 0.5*self.track_width + space
|
y = track.y*self.track_width - 0.5*self.track_width + 0.5*self.track_space
|
||||||
ll = snap_to_grid(vector(x,y))
|
ll = snap_to_grid(vector(x,y))
|
||||||
|
|
||||||
# calculate upper right
|
# calculate upper right
|
||||||
x = track.x*self.track_width + 0.5*self.track_width - space
|
x = track.x*self.track_width + 0.5*self.track_width - 0.5*self.track_space
|
||||||
y = track.y*self.track_width + 0.5*self.track_width - space
|
y = track.y*self.track_width + 0.5*self.track_width - 0.5*self.track_space
|
||||||
ur = snap_to_grid(vector(x,y))
|
ur = snap_to_grid(vector(x,y))
|
||||||
|
|
||||||
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
|
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
def convert_track_to_shape_pin(self, track):
|
||||||
|
"""
|
||||||
|
Convert a grid point into a rectangle shape that occupies the entire centered
|
||||||
|
track.
|
||||||
|
"""
|
||||||
|
# to scale coordinates to tracks
|
||||||
|
x = track[0]*self.track_width - 0.5*self.track_width
|
||||||
|
y = track[1]*self.track_width - 0.5*self.track_width
|
||||||
|
# offset lowest corner object to to (-track halo,-track halo)
|
||||||
|
ll = snap_to_grid(vector(x,y))
|
||||||
|
ur = snap_to_grid(ll + vector(self.track_width,self.track_width))
|
||||||
|
|
||||||
|
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
|
||||||
|
return p
|
||||||
|
|
||||||
def convert_track_to_shape(self, track):
|
def convert_track_to_shape(self, track):
|
||||||
"""
|
"""
|
||||||
Convert a grid point into a rectangle shape that occupies the entire centered
|
Convert a grid point into a rectangle shape that occupies the entire centered
|
||||||
|
|
@ -686,6 +653,23 @@ class router(router_tech):
|
||||||
|
|
||||||
return [ll,ur]
|
return [ll,ur]
|
||||||
|
|
||||||
|
def convert_track_to_inflated_pin(self, track):
|
||||||
|
"""
|
||||||
|
Convert a grid point into a rectangle shape that is inflated by a half DRC space.
|
||||||
|
"""
|
||||||
|
# calculate lower left
|
||||||
|
x = track.x*self.track_width - 0.5*self.track_width - 0.5*self.track_space
|
||||||
|
y = track.y*self.track_width - 0.5*self.track_width - 0.5*self.track_space
|
||||||
|
ll = snap_to_grid(vector(x,y))
|
||||||
|
|
||||||
|
# calculate upper right
|
||||||
|
x = track.x*self.track_width + 0.5*self.track_width + 0.5*self.track_space
|
||||||
|
y = track.y*self.track_width + 0.5*self.track_width + 0.5*self.track_space
|
||||||
|
ur = snap_to_grid(vector(x,y))
|
||||||
|
|
||||||
|
p = pin_layout("", [ll, ur], self.get_layer(track[2]))
|
||||||
|
return p
|
||||||
|
|
||||||
def analyze_pins(self, pin_name):
|
def analyze_pins(self, pin_name):
|
||||||
"""
|
"""
|
||||||
Analyze the shapes of a pin and combine them into groups which are connected.
|
Analyze the shapes of a pin and combine them into groups which are connected.
|
||||||
|
|
@ -742,8 +726,6 @@ class router(router_tech):
|
||||||
pg.enclose_pin()
|
pg.enclose_pin()
|
||||||
pg.add_enclosure(self.cell)
|
pg.add_enclosure(self.cell)
|
||||||
|
|
||||||
#self.write_debug_gds("pin_debug.gds", False)
|
|
||||||
|
|
||||||
def add_source(self, pin_name):
|
def add_source(self, pin_name):
|
||||||
"""
|
"""
|
||||||
This will mark the grids for all pin components as a source.
|
This will mark the grids for all pin components as a source.
|
||||||
|
|
@ -813,9 +795,6 @@ class router(router_tech):
|
||||||
"""
|
"""
|
||||||
debug.info(4,"Set path: " + str(path))
|
debug.info(4,"Set path: " + str(path))
|
||||||
|
|
||||||
# Keep track of path for future blockages
|
|
||||||
#path.set_blocked()
|
|
||||||
|
|
||||||
# This is marked for debug
|
# This is marked for debug
|
||||||
path.set_path()
|
path.set_path()
|
||||||
|
|
||||||
|
|
@ -877,8 +856,6 @@ class router(router_tech):
|
||||||
Enclose the tracks from ll to ur in a single rectangle that meets
|
Enclose the tracks from ll to ur in a single rectangle that meets
|
||||||
the track DRC rules.
|
the track DRC rules.
|
||||||
"""
|
"""
|
||||||
# Get the layer information
|
|
||||||
(width, space) = self.get_layer_width_space(zindex)
|
|
||||||
layer = self.get_layer(zindex)
|
layer = self.get_layer(zindex)
|
||||||
|
|
||||||
# This finds the pin shape enclosed by the track with DRC spacing on the sides
|
# This finds the pin shape enclosed by the track with DRC spacing on the sides
|
||||||
|
|
@ -886,44 +863,10 @@ class router(router_tech):
|
||||||
(abs_ll,unused) = pin.rect
|
(abs_ll,unused) = pin.rect
|
||||||
pin = self.convert_track_to_pin(ur)
|
pin = self.convert_track_to_pin(ur)
|
||||||
(unused,abs_ur) = pin.rect
|
(unused,abs_ur) = pin.rect
|
||||||
#print("enclose ll={0} ur={1}".format(ll,ur))
|
|
||||||
#print("enclose ll={0} ur={1}".format(abs_ll,abs_ur))
|
|
||||||
|
|
||||||
pin = pin_layout(name, [abs_ll, abs_ur], layer)
|
pin = pin_layout(name, [abs_ll, abs_ur], layer)
|
||||||
|
|
||||||
return pin
|
return pin
|
||||||
|
|
||||||
|
|
||||||
def compute_wide_enclosure(self, ll, ur, zindex, name=""):
|
|
||||||
"""
|
|
||||||
Enclose the tracks from ll to ur in a single rectangle that meets the track DRC rules.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Find the pin enclosure of the whole track shape (ignoring DRCs)
|
|
||||||
(abs_ll,unused) = self.convert_track_to_shape(ll)
|
|
||||||
(unused,abs_ur) = self.convert_track_to_shape(ur)
|
|
||||||
|
|
||||||
# Get the layer information
|
|
||||||
x_distance = abs(abs_ll.x-abs_ur.x)
|
|
||||||
y_distance = abs(abs_ll.y-abs_ur.y)
|
|
||||||
shape_width = min(x_distance, y_distance)
|
|
||||||
shape_length = max(x_distance, y_distance)
|
|
||||||
|
|
||||||
# Get the DRC rule for the grid dimensions
|
|
||||||
(width, space) = self.get_layer_width_space(zindex, shape_width, shape_length)
|
|
||||||
layer = self.get_layer(zindex)
|
|
||||||
|
|
||||||
if zindex==0:
|
|
||||||
spacing = vector(0.5*self.track_width, 0.5*space)
|
|
||||||
else:
|
|
||||||
spacing = vector(0.5*space, 0.5*self.track_width)
|
|
||||||
# Compute the shape offsets with correct spacing
|
|
||||||
new_ll = abs_ll + spacing
|
|
||||||
new_ur = abs_ur - spacing
|
|
||||||
pin = pin_layout(name, [new_ll, new_ur], layer)
|
|
||||||
|
|
||||||
return pin
|
|
||||||
|
|
||||||
|
|
||||||
def contract_path(self,path):
|
def contract_path(self,path):
|
||||||
"""
|
"""
|
||||||
|
|
@ -963,8 +906,7 @@ class router(router_tech):
|
||||||
self.add_route(path)
|
self.add_route(path)
|
||||||
|
|
||||||
path_set = grid_utils.flatten_set(path)
|
path_set = grid_utils.flatten_set(path)
|
||||||
inflated_path = grid_utils.inflate_set(path_set,self.supply_rail_space_width)
|
self.path_blockages.append(path_set)
|
||||||
self.path_blockages.append(inflated_path)
|
|
||||||
else:
|
else:
|
||||||
self.write_debug_gds("failed_route.gds")
|
self.write_debug_gds("failed_route.gds")
|
||||||
# clean up so we can try a reroute
|
# clean up so we can try a reroute
|
||||||
|
|
|
||||||
|
|
@ -3,48 +3,62 @@ from contact import contact
|
||||||
from pin_group import pin_group
|
from pin_group import pin_group
|
||||||
from vector import vector
|
from vector import vector
|
||||||
import debug
|
import debug
|
||||||
|
import math
|
||||||
|
|
||||||
class router_tech:
|
class router_tech:
|
||||||
"""
|
"""
|
||||||
This is a class to hold the router tech constants.
|
This is a class to hold the router tech constants.
|
||||||
"""
|
"""
|
||||||
def __init__(self, layers):
|
def __init__(self, layers, rail_track_width):
|
||||||
"""
|
"""
|
||||||
Allows us to change the layers that we are routing on. First layer
|
Allows us to change the layers that we are routing on. First layer
|
||||||
is always horizontal, middle is via, and last is always
|
is always horizontal, middle is via, and last is always
|
||||||
vertical.
|
vertical.
|
||||||
"""
|
"""
|
||||||
self.layers = layers
|
self.layers = layers
|
||||||
|
self.rail_track_width = rail_track_width
|
||||||
|
|
||||||
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
|
(self.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers
|
||||||
|
|
||||||
# This is the minimum routed track spacing
|
# This is the minimum routed track spacing
|
||||||
via_connect = contact(self.layers, (1, 1))
|
via_connect = contact(self.layers, (1, 1))
|
||||||
self.max_via_size = max(via_connect.width,via_connect.height)
|
max_via_size = max(via_connect.width,via_connect.height)
|
||||||
|
|
||||||
self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name))
|
self.horiz_layer_number = layer[self.horiz_layer_name]
|
||||||
self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name))
|
|
||||||
self.vert_layer_number = layer[self.vert_layer_name]
|
self.vert_layer_number = layer[self.vert_layer_name]
|
||||||
|
|
||||||
self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name))
|
if self.rail_track_width>1:
|
||||||
self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name))
|
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
|
||||||
self.horiz_layer_number = layer[self.horiz_layer_name]
|
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
|
||||||
|
|
||||||
self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing
|
|
||||||
self.vert_track_width = self.max_via_size + self.vert_layer_spacing
|
|
||||||
|
|
||||||
|
# For supplies, we will make the wire wider than the vias
|
||||||
|
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
|
||||||
|
self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size)
|
||||||
|
|
||||||
|
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
|
||||||
|
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
|
||||||
|
|
||||||
|
else:
|
||||||
|
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
|
||||||
|
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
|
||||||
|
|
||||||
|
self.horiz_track_width = max_via_size + self.horiz_layer_spacing
|
||||||
|
self.vert_track_width = max_via_size + self.vert_layer_spacing
|
||||||
|
|
||||||
# We'll keep horizontal and vertical tracks the same for simplicity.
|
# We'll keep horizontal and vertical tracks the same for simplicity.
|
||||||
self.track_width = max(self.horiz_track_width,self.vert_track_width)
|
self.track_width = max(self.horiz_track_width,self.vert_track_width)
|
||||||
debug.info(1,"Track width: "+str(self.track_width))
|
debug.info(1,"Track width: {:.3f}".format(self.track_width))
|
||||||
|
self.track_space = max(self.horiz_layer_spacing,self.vert_layer_spacing)
|
||||||
|
debug.info(1,"Track space: {:.3f}".format(self.track_space))
|
||||||
|
self.track_wire = self.track_width - self.track_space
|
||||||
|
debug.info(1,"Track wire width: {:.3f}".format(self.track_wire))
|
||||||
|
|
||||||
self.track_widths = vector([self.track_width] * 2)
|
self.track_widths = vector([self.track_width] * 2)
|
||||||
self.track_factor = vector([1/self.track_width] * 2)
|
self.track_factor = vector([1/self.track_width] * 2)
|
||||||
debug.info(2,"Track factor: {0}".format(self.track_factor))
|
debug.info(2,"Track factor: {}".format(self.track_factor))
|
||||||
|
|
||||||
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
|
|
||||||
self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
|
||||||
|
self.layer_widths = [self.track_wire, 1, self.track_wire]
|
||||||
|
|
||||||
def get_zindex(self,layer_num):
|
def get_zindex(self,layer_num):
|
||||||
if layer_num==self.horiz_layer_number:
|
if layer_num==self.horiz_layer_number:
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -76,4 +90,24 @@ class router_tech:
|
||||||
|
|
||||||
return (min_width,min_spacing)
|
return (min_width,min_spacing)
|
||||||
|
|
||||||
|
|
||||||
|
def get_supply_layer_width_space(self, zindex):
|
||||||
|
"""
|
||||||
|
These are the width and spacing of a supply layer given a supply rail
|
||||||
|
of the given number of min wire widths.
|
||||||
|
"""
|
||||||
|
if zindex==1:
|
||||||
|
layer_name = self.vert_layer_name
|
||||||
|
elif zindex==0:
|
||||||
|
layer_name = self.horiz_layer_name
|
||||||
|
else:
|
||||||
|
debug.error("Invalid zindex for track", -1)
|
||||||
|
|
||||||
|
min_wire_width = drc("minwidth_{0}".format(layer_name), 0, math.inf)
|
||||||
|
|
||||||
|
min_width = drc("minwidth_{0}".format(layer_name), self.rail_track_width*min_wire_width, math.inf)
|
||||||
|
min_spacing = drc(str(layer_name)+"_to_"+str(layer_name), self.rail_track_width*min_wire_width, math.inf)
|
||||||
|
|
||||||
|
return (min_width,min_spacing)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,17 +24,16 @@ class supply_router(router):
|
||||||
This will route on layers in design. It will get the blockages from
|
This will route on layers in design. It will get the blockages from
|
||||||
either the gds file name or the design itself (by saving to a gds file).
|
either the gds file name or the design itself (by saving to a gds file).
|
||||||
"""
|
"""
|
||||||
router.__init__(self, layers, design, gds_filename)
|
# Power rail width in minimum wire widths
|
||||||
|
self.rail_track_width = 3
|
||||||
|
|
||||||
|
router.__init__(self, layers, design, gds_filename, self.rail_track_width)
|
||||||
|
|
||||||
# The list of supply rails (grid sets) that may be routed
|
# The list of supply rails (grid sets) that may be routed
|
||||||
self.supply_rails = {}
|
self.supply_rails = {}
|
||||||
self.supply_rail_wires = {}
|
|
||||||
# This is the same as above but as a sigle set for the all the rails
|
# This is the same as above but as a sigle set for the all the rails
|
||||||
self.supply_rail_tracks = {}
|
self.supply_rail_tracks = {}
|
||||||
self.supply_rail_wire_tracks = {}
|
|
||||||
|
|
||||||
# Power rail width in grid units.
|
|
||||||
self.rail_track_width = 2
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -65,17 +64,10 @@ class supply_router(router):
|
||||||
# but this is simplest for now.
|
# but this is simplest for now.
|
||||||
self.create_routing_grid()
|
self.create_routing_grid()
|
||||||
|
|
||||||
# Compute the grid dimensions
|
|
||||||
self.compute_supply_rail_dimensions()
|
|
||||||
|
|
||||||
# Get the pin shapes
|
# Get the pin shapes
|
||||||
#start_time = datetime.now()
|
|
||||||
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
|
||||||
#print_time("Pins and blockages",datetime.now(), start_time)
|
|
||||||
#self.write_debug_gds("pin_enclosures.gds",stop_program=True)
|
|
||||||
|
|
||||||
# Add the supply rails in a mesh network and connect H/V with vias
|
# Add the supply rails in a mesh network and connect H/V with vias
|
||||||
#start_time = datetime.now()
|
|
||||||
# Block everything
|
# Block everything
|
||||||
self.prepare_blockages(self.gnd_name)
|
self.prepare_blockages(self.gnd_name)
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
|
|
@ -85,23 +77,15 @@ class supply_router(router):
|
||||||
self.prepare_blockages(self.vdd_name)
|
self.prepare_blockages(self.vdd_name)
|
||||||
# Determine the rail locations
|
# Determine the rail locations
|
||||||
self.route_supply_rails(self.vdd_name,1)
|
self.route_supply_rails(self.vdd_name,1)
|
||||||
#self.write_debug_gds("debug_rails.gds",stop_program=True)
|
|
||||||
#print_time("Supply rails",datetime.now(), start_time)
|
|
||||||
|
|
||||||
#start_time = datetime.now()
|
|
||||||
self.route_simple_overlaps(vdd_name)
|
self.route_simple_overlaps(vdd_name)
|
||||||
self.route_simple_overlaps(gnd_name)
|
self.route_simple_overlaps(gnd_name)
|
||||||
#print_time("Simple overlaps",datetime.now(), start_time)
|
|
||||||
#self.write_debug_gds("debug_simple_route.gds",stop_program=False)
|
|
||||||
|
|
||||||
# Route the supply pins to the supply rails
|
# Route the supply pins to the supply rails
|
||||||
# Route vdd first since we want it to be shorter
|
# Route vdd first since we want it to be shorter
|
||||||
#start_time = datetime.now()
|
|
||||||
self.route_pins_to_rails(vdd_name)
|
self.route_pins_to_rails(vdd_name)
|
||||||
self.route_pins_to_rails(gnd_name)
|
self.route_pins_to_rails(gnd_name)
|
||||||
#print_time("Routing",datetime.now(), start_time)
|
|
||||||
#self.write_debug_gds("debug_pin_routes.gds",stop_program=True)
|
|
||||||
|
|
||||||
#self.write_debug_gds("final.gds",False)
|
#self.write_debug_gds("final.gds",False)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
@ -116,10 +100,8 @@ class supply_router(router):
|
||||||
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name))
|
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name))
|
||||||
|
|
||||||
# These are the wire tracks
|
# These are the wire tracks
|
||||||
wire_tracks = self.supply_rail_wire_tracks[pin_name]
|
wire_tracks = self.supply_rail_tracks[pin_name]
|
||||||
# These are the wire and space tracks
|
routed_count=0
|
||||||
supply_tracks = self.supply_rail_tracks[pin_name]
|
|
||||||
|
|
||||||
for pg in self.pin_groups[pin_name]:
|
for pg in self.pin_groups[pin_name]:
|
||||||
if pg.is_routed():
|
if pg.is_routed():
|
||||||
continue
|
continue
|
||||||
|
|
@ -127,17 +109,15 @@ class supply_router(router):
|
||||||
# First, check if we just overlap, if so, we are done.
|
# First, check if we just overlap, if so, we are done.
|
||||||
overlap_grids = wire_tracks & pg.grids
|
overlap_grids = wire_tracks & pg.grids
|
||||||
if len(overlap_grids)>0:
|
if len(overlap_grids)>0:
|
||||||
|
routed_count += 1
|
||||||
pg.set_routed()
|
pg.set_routed()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Else, if we overlap some of the space track, we can patch it with an enclosure
|
|
||||||
common_set = supply_tracks & pg.grids
|
|
||||||
if len(common_set)>0:
|
|
||||||
pg.create_simple_overlap_enclosure(common_set)
|
|
||||||
pg.add_enclosure(self.cell)
|
|
||||||
|
|
||||||
|
# Else, if we overlap some of the space track, we can patch it with an enclosure
|
||||||
|
#pg.create_simple_overlap_enclosure(pg.grids)
|
||||||
|
#pg.add_enclosure(self.cell)
|
||||||
|
|
||||||
|
debug.info(1,"Routed {} simple overlap pins".format(routed_count))
|
||||||
|
|
||||||
def finalize_supply_rails(self, name):
|
def finalize_supply_rails(self, name):
|
||||||
"""
|
"""
|
||||||
|
|
@ -146,7 +126,7 @@ class supply_router(router):
|
||||||
NOTE: It is still possible though unlikely that there are disconnected groups of rails.
|
NOTE: It is still possible though unlikely that there are disconnected groups of rails.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
all_rails = self.supply_rail_wires[name]
|
all_rails = self.supply_rails[name]
|
||||||
|
|
||||||
connections = set()
|
connections = set()
|
||||||
via_areas = []
|
via_areas = []
|
||||||
|
|
@ -186,8 +166,8 @@ class supply_router(router):
|
||||||
# the indices to determine a rail is connected to another
|
# the indices to determine a rail is connected to another
|
||||||
# the overlap area for placement of a via
|
# the overlap area for placement of a via
|
||||||
overlap = new_r1 & new_r2
|
overlap = new_r1 & new_r2
|
||||||
if len(overlap) >= self.supply_rail_wire_width**2:
|
if len(overlap) >= 1:
|
||||||
debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap))
|
debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap))
|
||||||
connections.update([i1,i2])
|
connections.update([i1,i2])
|
||||||
via_areas.append(overlap)
|
via_areas.append(overlap)
|
||||||
|
|
||||||
|
|
@ -196,7 +176,7 @@ class supply_router(router):
|
||||||
ll = grid_utils.get_lower_left(area)
|
ll = grid_utils.get_lower_left(area)
|
||||||
ur = grid_utils.get_upper_right(area)
|
ur = grid_utils.get_upper_right(area)
|
||||||
center = (ll + ur).scale(0.5,0.5,0)
|
center = (ll + ur).scale(0.5,0.5,0)
|
||||||
self.add_via(center,self.rail_track_width)
|
self.add_via(center,1)
|
||||||
|
|
||||||
# Determien which indices were not connected to anything above
|
# Determien which indices were not connected to anything above
|
||||||
missing_indices = set([x for x in range(len(self.supply_rails[name]))])
|
missing_indices = set([x for x in range(len(self.supply_rails[name]))])
|
||||||
|
|
@ -209,7 +189,6 @@ class supply_router(router):
|
||||||
ur = grid_utils.get_upper_right(all_rails[rail_index])
|
ur = grid_utils.get_upper_right(all_rails[rail_index])
|
||||||
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
|
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
|
||||||
self.supply_rails[name].pop(rail_index)
|
self.supply_rails[name].pop(rail_index)
|
||||||
self.supply_rail_wires[name].pop(rail_index)
|
|
||||||
|
|
||||||
# Make the supply rails into a big giant set of grids for easy blockages.
|
# Make the supply rails into a big giant set of grids for easy blockages.
|
||||||
# Must be done after we determine which ones are connected.
|
# Must be done after we determine which ones are connected.
|
||||||
|
|
@ -226,7 +205,7 @@ class supply_router(router):
|
||||||
ll = grid_utils.get_lower_left(rail)
|
ll = grid_utils.get_lower_left(rail)
|
||||||
ur = grid_utils.get_upper_right(rail)
|
ur = grid_utils.get_upper_right(rail)
|
||||||
z = ll.z
|
z = ll.z
|
||||||
pin = self.compute_wide_enclosure(ll, ur, z, name)
|
pin = self.compute_pin_enclosure(ll, ur, z, name)
|
||||||
debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin))
|
debug.info(2,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin))
|
||||||
self.cell.add_layout_pin(text=name,
|
self.cell.add_layout_pin(text=name,
|
||||||
layer=pin.layer,
|
layer=pin.layer,
|
||||||
|
|
@ -234,43 +213,6 @@ class supply_router(router):
|
||||||
width=pin.width(),
|
width=pin.width(),
|
||||||
height=pin.height())
|
height=pin.height())
|
||||||
|
|
||||||
def compute_supply_rail_dimensions(self):
|
|
||||||
"""
|
|
||||||
Compute the supply rail dimensions including wide metal spacing rules.
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.max_yoffset = self.rg.ur.y
|
|
||||||
self.max_xoffset = self.rg.ur.x
|
|
||||||
|
|
||||||
# Longest length is conservative
|
|
||||||
rail_length = max(self.max_yoffset,self.max_xoffset)
|
|
||||||
# Convert the number of tracks to dimensions to get the design rule spacing
|
|
||||||
rail_width = self.track_width*self.rail_track_width
|
|
||||||
|
|
||||||
# Get the conservative width and spacing of the top rails
|
|
||||||
(horizontal_width, horizontal_space) = self.get_layer_width_space(0, rail_width, rail_length)
|
|
||||||
(vertical_width, vertical_space) = self.get_layer_width_space(1, rail_width, rail_length)
|
|
||||||
width = max(horizontal_width, vertical_width)
|
|
||||||
space = max(horizontal_space, vertical_space)
|
|
||||||
|
|
||||||
# This is the supply rail pitch in terms of routing grids
|
|
||||||
# i.e. a rail of self.rail_track_width needs this many tracks including
|
|
||||||
# space
|
|
||||||
track_pitch = self.rail_track_width*width + space
|
|
||||||
|
|
||||||
# Determine the pitch (in tracks) of the rail wire + spacing
|
|
||||||
self.supply_rail_width = math.ceil(track_pitch/self.track_width)
|
|
||||||
debug.info(1,"Rail step: {}".format(self.supply_rail_width))
|
|
||||||
|
|
||||||
# Conservatively determine the number of tracks that the rail actually occupies
|
|
||||||
space_tracks = math.ceil(space/self.track_width)
|
|
||||||
self.supply_rail_wire_width = self.supply_rail_width - space_tracks
|
|
||||||
debug.info(1,"Rail wire tracks: {}".format(self.supply_rail_wire_width))
|
|
||||||
total_space = self.supply_rail_width - self.supply_rail_wire_width
|
|
||||||
self.supply_rail_space_width = math.floor(0.5*total_space)
|
|
||||||
debug.info(1,"Rail space tracks: {} (on both sides)".format(self.supply_rail_space_width))
|
|
||||||
|
|
||||||
|
|
||||||
def compute_supply_rails(self, name, supply_number):
|
def compute_supply_rails(self, name, supply_number):
|
||||||
"""
|
"""
|
||||||
Compute the unblocked locations for the horizontal and vertical supply rails.
|
Compute the unblocked locations for the horizontal and vertical supply rails.
|
||||||
|
|
@ -279,16 +221,20 @@ class supply_router(router):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.supply_rails[name]=[]
|
self.supply_rails[name]=[]
|
||||||
self.supply_rail_wires[name]=[]
|
|
||||||
|
|
||||||
start_offset = supply_number*self.supply_rail_width
|
max_yoffset = self.rg.ur.y
|
||||||
|
max_xoffset = self.rg.ur.x
|
||||||
|
min_yoffset = self.rg.ll.y
|
||||||
|
min_xoffset = self.rg.ll.x
|
||||||
|
|
||||||
|
|
||||||
# Horizontal supply rails
|
# Horizontal supply rails
|
||||||
for offset in range(start_offset, self.max_yoffset, 2*self.supply_rail_width):
|
start_offset = min_yoffset + supply_number
|
||||||
|
for offset in range(start_offset, max_yoffset, 2):
|
||||||
# Seed the function at the location with the given width
|
# Seed the function at the location with the given width
|
||||||
wave = [vector3d(0,offset+i,0) for i in range(self.supply_rail_width)]
|
wave = [vector3d(min_xoffset,offset,0)]
|
||||||
# While we can keep expanding east in this horizontal track
|
# While we can keep expanding east in this horizontal track
|
||||||
while wave and wave[0].x < self.max_xoffset:
|
while wave and wave[0].x < max_xoffset:
|
||||||
added_rail = self.find_supply_rail(name, wave, direction.EAST)
|
added_rail = self.find_supply_rail(name, wave, direction.EAST)
|
||||||
if not added_rail:
|
if not added_rail:
|
||||||
# Just seed with the next one
|
# Just seed with the next one
|
||||||
|
|
@ -298,12 +244,12 @@ class supply_router(router):
|
||||||
wave = added_rail.neighbor(direction.EAST)
|
wave = added_rail.neighbor(direction.EAST)
|
||||||
|
|
||||||
# Vertical supply rails
|
# Vertical supply rails
|
||||||
max_offset = self.rg.ur.x
|
start_offset = min_xoffset + supply_number
|
||||||
for offset in range(start_offset, self.max_xoffset, 2*self.supply_rail_width):
|
for offset in range(start_offset, max_xoffset, 2):
|
||||||
# Seed the function at the location with the given width
|
# Seed the function at the location with the given width
|
||||||
wave = [vector3d(offset+i,0,1) for i in range(self.supply_rail_width)]
|
wave = [vector3d(offset,min_yoffset,1)]
|
||||||
# While we can keep expanding north in this vertical track
|
# While we can keep expanding north in this vertical track
|
||||||
while wave and wave[0].y < self.max_yoffset:
|
while wave and wave[0].y < max_yoffset:
|
||||||
added_rail = self.find_supply_rail(name, wave, direction.NORTH)
|
added_rail = self.find_supply_rail(name, wave, direction.NORTH)
|
||||||
if not added_rail:
|
if not added_rail:
|
||||||
# Just seed with the next one
|
# Just seed with the next one
|
||||||
|
|
@ -378,11 +324,6 @@ class supply_router(router):
|
||||||
if len(wave_path)>=4*self.rail_track_width:
|
if len(wave_path)>=4*self.rail_track_width:
|
||||||
grid_set = wave_path.get_grids()
|
grid_set = wave_path.get_grids()
|
||||||
self.supply_rails[name].append(grid_set)
|
self.supply_rails[name].append(grid_set)
|
||||||
|
|
||||||
start_wire_index = self.supply_rail_space_width
|
|
||||||
end_wire_index = self.supply_rail_width - self.supply_rail_space_width
|
|
||||||
wire_set = wave_path.get_wire_grids(start_wire_index,end_wire_index)
|
|
||||||
self.supply_rail_wires[name].append(wire_set)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
@ -417,10 +358,6 @@ class supply_router(router):
|
||||||
rail_set.update(rail)
|
rail_set.update(rail)
|
||||||
self.supply_rail_tracks[pin_name] = rail_set
|
self.supply_rail_tracks[pin_name] = rail_set
|
||||||
|
|
||||||
wire_set = set()
|
|
||||||
for rail in self.supply_rail_wires[pin_name]:
|
|
||||||
wire_set.update(rail)
|
|
||||||
self.supply_rail_wire_tracks[pin_name] = wire_set
|
|
||||||
|
|
||||||
|
|
||||||
def route_pins_to_rails(self, pin_name):
|
def route_pins_to_rails(self, pin_name):
|
||||||
|
|
@ -430,8 +367,8 @@ class supply_router(router):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
|
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
|
||||||
debug.info(1,"Routing {0} with {1} pin components to route.".format(pin_name,
|
debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name,
|
||||||
remaining_components))
|
remaining_components))
|
||||||
|
|
||||||
for index,pg in enumerate(self.pin_groups[pin_name]):
|
for index,pg in enumerate(self.pin_groups[pin_name]):
|
||||||
if pg.is_routed():
|
if pg.is_routed():
|
||||||
|
|
@ -465,7 +402,7 @@ class supply_router(router):
|
||||||
"""
|
"""
|
||||||
debug.info(4,"Add supply rail target {}".format(pin_name))
|
debug.info(4,"Add supply rail target {}".format(pin_name))
|
||||||
# Add the wire itself as the target
|
# Add the wire itself as the target
|
||||||
self.rg.set_target(self.supply_rail_wire_tracks[pin_name])
|
self.rg.set_target(self.supply_rail_tracks[pin_name])
|
||||||
# But unblock all the rail tracks including the space
|
# But unblock all the rail tracks including the space
|
||||||
self.rg.set_blocked(self.supply_rail_tracks[pin_name],False)
|
self.rg.set_blocked(self.supply_rail_tracks[pin_name],False)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ class no_blockages_test(openram_test):
|
||||||
if False:
|
if False:
|
||||||
from control_logic import control_logic
|
from control_logic import control_logic
|
||||||
cell = control_logic(16)
|
cell = control_logic(16)
|
||||||
|
layer_stack =("metal3","via3","metal4")
|
||||||
|
rtr=router(layer_stack, cell)
|
||||||
|
self.assertTrue(rtr.route())
|
||||||
else:
|
else:
|
||||||
from sram import sram
|
from sram import sram
|
||||||
from sram_config import sram_config
|
from sram_config import sram_config
|
||||||
|
|
@ -33,9 +36,6 @@ class no_blockages_test(openram_test):
|
||||||
sram = sram(c, "sram1")
|
sram = sram(c, "sram1")
|
||||||
cell = sram.s
|
cell = sram.s
|
||||||
|
|
||||||
layer_stack =("metal3","via3","metal4")
|
|
||||||
rtr=router(layer_stack, cell)
|
|
||||||
self.assertTrue(rtr.route())
|
|
||||||
self.local_check(cell,True)
|
self.local_check(cell,True)
|
||||||
|
|
||||||
# fails if there are any DRC errors on any cells
|
# fails if there are any DRC errors on any cells
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ class sram():
|
||||||
"""
|
"""
|
||||||
def __init__(self, sram_config, name):
|
def __init__(self, sram_config, name):
|
||||||
|
|
||||||
sram_config.compute_sizes()
|
|
||||||
sram_config.set_local_config(self)
|
sram_config.set_local_config(self)
|
||||||
|
|
||||||
# reset the static duplicate name checker for unit tests
|
# reset the static duplicate name checker for unit tests
|
||||||
|
|
@ -34,8 +33,6 @@ class sram():
|
||||||
from sram_1bank import sram_1bank as sram
|
from sram_1bank import sram_1bank as sram
|
||||||
elif self.num_banks == 2:
|
elif self.num_banks == 2:
|
||||||
from sram_2bank import sram_2bank as sram
|
from sram_2bank import sram_2bank as sram
|
||||||
elif self.num_banks == 4:
|
|
||||||
from sram_4bank import sram_4bank as sram
|
|
||||||
else:
|
else:
|
||||||
debug.error("Invalid number of banks.",-1)
|
debug.error("Invalid number of banks.",-1)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ from globals import OPTS, print_time
|
||||||
|
|
||||||
from sram_base import sram_base
|
from sram_base import sram_base
|
||||||
from bank import bank
|
from bank import bank
|
||||||
|
from contact import m2m3
|
||||||
from dff_buf_array import dff_buf_array
|
from dff_buf_array import dff_buf_array
|
||||||
from dff_array import dff_array
|
from dff_array import dff_array
|
||||||
|
|
||||||
|
|
@ -21,11 +22,6 @@ class sram_1bank(sram_base):
|
||||||
def __init__(self, name, sram_config):
|
def __init__(self, name, sram_config):
|
||||||
sram_base.__init__(self, name, sram_config)
|
sram_base.__init__(self, name, sram_config)
|
||||||
|
|
||||||
|
|
||||||
def create_netlist(self):
|
|
||||||
sram_base.create_netlist(self)
|
|
||||||
self.create_modules()
|
|
||||||
|
|
||||||
def create_modules(self):
|
def create_modules(self):
|
||||||
"""
|
"""
|
||||||
This adds the modules for a single bank SRAM with control
|
This adds the modules for a single bank SRAM with control
|
||||||
|
|
@ -61,15 +57,18 @@ class sram_1bank(sram_base):
|
||||||
row_addr_pos = [None]*len(self.all_ports)
|
row_addr_pos = [None]*len(self.all_ports)
|
||||||
col_addr_pos = [None]*len(self.all_ports)
|
col_addr_pos = [None]*len(self.all_ports)
|
||||||
data_pos = [None]*len(self.all_ports)
|
data_pos = [None]*len(self.all_ports)
|
||||||
|
|
||||||
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
|
||||||
data_gap = self.m2_pitch*(self.word_size+1)
|
|
||||||
|
|
||||||
|
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
|
||||||
|
# The M1 pitch is for supply rail spacings
|
||||||
|
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch
|
||||||
|
|
||||||
# Port 0
|
# Port 0
|
||||||
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.
|
||||||
|
# It is also placed to align with the column decoder (if it exists hence the bank gap)
|
||||||
control_pos[port] = vector(-self.control_logic_insts[port].width - 2*self.m2_pitch,
|
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.bank.m2_gap)
|
||||||
self.control_logic_insts[port].place(control_pos[port])
|
self.control_logic_insts[port].place(control_pos[port])
|
||||||
|
|
||||||
# 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.
|
||||||
|
|
@ -81,8 +80,8 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Add the col address flops below the bank to the left of the lower-left of bank array
|
# Add the col address flops below the bank to the left of the lower-left of bank array
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.central_bus_width,
|
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
|
||||||
-data_gap - self.col_addr_dff_insts[port].height)
|
-max_gap_size - self.col_addr_dff_insts[port].height)
|
||||||
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
self.col_addr_dff_insts[port].place(col_addr_pos[port])
|
||||||
|
|
||||||
# Add the data flops below the bank to the right of the lower-left of bank array
|
# Add the data flops below the bank to the right of the lower-left of bank array
|
||||||
|
|
@ -92,16 +91,18 @@ class sram_1bank(sram_base):
|
||||||
# sense amps.
|
# sense amps.
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
data_pos[port] = vector(self.bank.bank_array_ll.x,
|
||||||
-data_gap - self.data_dff_insts[port].height)
|
-max_gap_size - self.data_dff_insts[port].height)
|
||||||
self.data_dff_insts[port].place(data_pos[port])
|
self.data_dff_insts[port].place(data_pos[port])
|
||||||
|
|
||||||
|
|
||||||
if len(self.all_ports)>1:
|
if len(self.all_ports)>1:
|
||||||
# Port 1
|
# Port 1
|
||||||
port = 1
|
port = 1
|
||||||
|
|
||||||
# This includes 2 M2 pitches for the row addr clock line
|
# This includes 2 M2 pitches for the row addr clock line
|
||||||
|
# It is also placed to align with the column decoder (if it exists hence the bank gap)
|
||||||
control_pos[port] = vector(self.bank_inst.rx() + self.control_logic_insts[port].width + 2*self.m2_pitch,
|
control_pos[port] = vector(self.bank_inst.rx() + 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.bank.m2_gap)
|
||||||
self.control_logic_insts[port].place(control_pos[port], mirror="MY")
|
self.control_logic_insts[port].place(control_pos[port], mirror="MY")
|
||||||
|
|
||||||
# The row address bits are placed above the control logic aligned on the left.
|
# The row address bits are placed above the control logic aligned on the left.
|
||||||
|
|
@ -113,8 +114,8 @@ class sram_1bank(sram_base):
|
||||||
|
|
||||||
# Add the col address flops above the bank to the right of the upper-right of bank array
|
# Add the col address flops above the bank to the right of the upper-right of bank array
|
||||||
if self.col_addr_dff:
|
if self.col_addr_dff:
|
||||||
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.central_bus_width,
|
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
|
||||||
self.bank_inst.uy() + data_gap + self.col_addr_dff_insts[port].height)
|
self.bank.height + max_gap_size + self.col_addr_dff_insts[port].height)
|
||||||
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
|
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
|
||||||
|
|
||||||
# Add the data flops above the bank to the left of the upper-right of bank array
|
# Add the data flops above the bank to the left of the upper-right of bank array
|
||||||
|
|
@ -124,7 +125,7 @@ class sram_1bank(sram_base):
|
||||||
# sense amps.
|
# sense amps.
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
|
||||||
self.bank.uy() + data_gap + self.data_dff_insts[port].height)
|
self.bank.height + max_gap_size + self.data_dff_insts[port].height)
|
||||||
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -178,27 +179,11 @@ class sram_1bank(sram_base):
|
||||||
# Connect all of these clock pins to the clock in the central bus
|
# Connect all of these clock pins to the clock in the central bus
|
||||||
# This is something like a "spine" clock distribution. The two spines
|
# This is something like a "spine" clock distribution. The two spines
|
||||||
# are clk_buf and clk_buf_bar
|
# are clk_buf and clk_buf_bar
|
||||||
|
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
|
||||||
|
control_clk_buf_pos = control_clk_buf_pin.center()
|
||||||
|
|
||||||
bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf{}".format(port))
|
|
||||||
bank_clk_buf_pos = bank_clk_buf_pin.center()
|
|
||||||
bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar{}".format(port))
|
|
||||||
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
|
|
||||||
|
|
||||||
if self.col_addr_dff:
|
|
||||||
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
|
||||||
dff_clk_pos = dff_clk_pin.center()
|
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
|
|
||||||
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
|
||||||
|
|
||||||
if port in self.write_ports:
|
|
||||||
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
|
|
||||||
data_dff_clk_pos = data_dff_clk_pin.center()
|
|
||||||
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
|
|
||||||
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
|
|
||||||
|
|
||||||
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
|
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
|
||||||
# to route vertically. For port1, it is to the left.
|
# to route vertically. For port1, it is to the left.
|
||||||
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
|
|
||||||
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
|
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
|
||||||
if port%2:
|
if port%2:
|
||||||
control_clk_buf_pos = control_clk_buf_pin.lc()
|
control_clk_buf_pos = control_clk_buf_pin.lc()
|
||||||
|
|
@ -210,17 +195,40 @@ class sram_1bank(sram_base):
|
||||||
row_addr_clk_pos = row_addr_clk_pin.rc()
|
row_addr_clk_pos = row_addr_clk_pin.rc()
|
||||||
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
|
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
|
||||||
row_addr_clk_pos.y)
|
row_addr_clk_pos.y)
|
||||||
mid2_pos = vector(mid1_pos.x,
|
|
||||||
control_clk_buf_pos.y)
|
# This is the steiner point where the net branches out
|
||||||
# Note, the via to the control logic is taken care of when we route
|
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
|
||||||
# the control logic to the bank
|
self.add_path("metal1", [control_clk_buf_pos, clk_steiner_pos])
|
||||||
self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos])
|
self.add_via_center(layers=("metal1","via1","metal2"),
|
||||||
|
offset=clk_steiner_pos,
|
||||||
|
rotate=90)
|
||||||
|
|
||||||
|
# Note, the via to the control logic is taken care of above
|
||||||
|
self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
|
||||||
|
|
||||||
|
if self.col_addr_dff:
|
||||||
|
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
|
||||||
|
dff_clk_pos = dff_clk_pin.center()
|
||||||
|
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
|
||||||
|
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||||
|
|
||||||
|
if port in self.write_ports:
|
||||||
|
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
|
||||||
|
data_dff_clk_pos = data_dff_clk_pin.center()
|
||||||
|
mid_pos = vector(clk_steiner_pos.x, data_dff_clk_pos.y)
|
||||||
|
# In some designs, the steiner via will be too close to the mid_pos via
|
||||||
|
# so make the wire as wide as the contacts
|
||||||
|
self.add_path("metal2",[mid_pos, clk_steiner_pos], width=max(m2m3.width,m2m3.height))
|
||||||
|
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, clk_steiner_pos])
|
||||||
|
|
||||||
|
|
||||||
def route_control_logic(self):
|
def route_control_logic(self):
|
||||||
""" Route the outputs from the control logic module """
|
""" Route the outputs from the control logic module """
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
for signal in self.control_logic_outputs[port]:
|
for signal in self.control_logic_outputs[port]:
|
||||||
|
# The clock gets routed separately and is not a part of the bank
|
||||||
|
if "clk" in signal:
|
||||||
|
continue
|
||||||
src_pin = self.control_logic_insts[port].get_pin(signal)
|
src_pin = self.control_logic_insts[port].get_pin(signal)
|
||||||
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
|
||||||
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
|
||||||
|
|
@ -268,17 +276,20 @@ class sram_1bank(sram_base):
|
||||||
""" Connect the output of the data flops to the write driver """
|
""" Connect the output of the data flops to the write driver """
|
||||||
# This is where the channel will start (y-dimension at least)
|
# This is where the channel will start (y-dimension at least)
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
if port%2:
|
||||||
|
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
|
||||||
|
else:
|
||||||
|
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
|
||||||
|
|
||||||
|
|
||||||
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
|
||||||
|
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
|
||||||
|
|
||||||
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
|
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
|
||||||
|
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
|
||||||
route_map = list(zip(bank_names, dff_names))
|
|
||||||
dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names }
|
route_map = list(zip(bank_pins, dff_pins))
|
||||||
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
|
self.create_horizontal_channel_route(route_map, offset)
|
||||||
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
|
||||||
all_pins = {**dff_pins, **bank_pins}
|
|
||||||
self.create_horizontal_channel_route(route_map, all_pins, offset)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,331 +0,0 @@
|
||||||
import sys
|
|
||||||
from tech import drc, spice
|
|
||||||
import debug
|
|
||||||
from math import log,sqrt,ceil
|
|
||||||
import datetime
|
|
||||||
import getpass
|
|
||||||
from vector import vector
|
|
||||||
from globals import OPTS, print_time
|
|
||||||
|
|
||||||
from sram_base import sram_base
|
|
||||||
from bank import bank
|
|
||||||
from dff_buf_array import dff_buf_array
|
|
||||||
from dff_array import dff_array
|
|
||||||
|
|
||||||
class sram_4bank(sram_base):
|
|
||||||
"""
|
|
||||||
Procedures specific to a four bank SRAM.
|
|
||||||
"""
|
|
||||||
def __init__(self, name, sram_config):
|
|
||||||
sram_base.__init__(self, name, sram_config)
|
|
||||||
|
|
||||||
def compute_bank_offsets(self):
|
|
||||||
""" Compute the overall offsets for a four bank SRAM """
|
|
||||||
|
|
||||||
# The main difference is that the four bank SRAM has the data bus in the middle of the four banks
|
|
||||||
# as opposed to the top of the banks.
|
|
||||||
|
|
||||||
# In 4 bank SRAM, the height is determined by the bank decoder and address flop
|
|
||||||
self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \
|
|
||||||
+ self.supply_bus_height + self.msb_decoder.height + self.msb_address.width
|
|
||||||
# The address bus extends down through the power rails, but control and bank_sel bus don't
|
|
||||||
self.addr_bus_height = self.vertical_bus_height
|
|
||||||
|
|
||||||
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0)
|
|
||||||
self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance)
|
|
||||||
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height \
|
|
||||||
+ self.bank.height + 2*self.bank_to_bus_distance)
|
|
||||||
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
|
|
||||||
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
|
|
||||||
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
|
|
||||||
|
|
||||||
# Control is placed at the top above the control bus and everything
|
|
||||||
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
|
|
||||||
|
|
||||||
# Bank select flops get put to the right of control logic above bank1 and the buses
|
|
||||||
# Leave a pitch to get the vdd rails up to M2
|
|
||||||
self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch,
|
|
||||||
self.supply_bus_offset.y + self.supply_bus_height \
|
|
||||||
+ 2*self.m1_pitch + self.msb_address.width)
|
|
||||||
|
|
||||||
# Decoder goes above the MSB address flops, and is flipped in Y
|
|
||||||
# separate the two by a bank to bus distance for nwell rules, just in case
|
|
||||||
self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance)
|
|
||||||
|
|
||||||
|
|
||||||
def add_modules(self):
|
|
||||||
""" Adds the modules and the buses to the top level """
|
|
||||||
|
|
||||||
self.compute_bus_sizes()
|
|
||||||
|
|
||||||
self.add_banks()
|
|
||||||
|
|
||||||
self.compute_bank_offsets()
|
|
||||||
|
|
||||||
self.add_busses()
|
|
||||||
|
|
||||||
self.add_logic()
|
|
||||||
|
|
||||||
self.width = self.bank_inst[1].ur().x
|
|
||||||
self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy())
|
|
||||||
|
|
||||||
def add_banks(self):
|
|
||||||
|
|
||||||
# Placement of bank 0 (upper left)
|
|
||||||
bank_position_0 = vector(self.bank.width,
|
|
||||||
self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance)
|
|
||||||
self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)]
|
|
||||||
|
|
||||||
# Placement of bank 1 (upper right)
|
|
||||||
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
|
|
||||||
bank_position_1 = vector(x_off, bank_position_0.y)
|
|
||||||
self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1))
|
|
||||||
|
|
||||||
# Placement of bank 2 (bottom left)
|
|
||||||
y_off = self.bank.height
|
|
||||||
bank_position_2 = vector(bank_position_0.x, y_off)
|
|
||||||
self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1))
|
|
||||||
|
|
||||||
# Placement of bank 3 (bottom right)
|
|
||||||
bank_position_3 = vector(bank_position_1.x, bank_position_2.y)
|
|
||||||
self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def add_logic(self):
|
|
||||||
""" Add the control and MSB decode/bank select logic for four banks """
|
|
||||||
|
|
||||||
|
|
||||||
self.add_control_logic(position=self.control_logic_position)
|
|
||||||
|
|
||||||
self.msb_address_inst = self.add_inst(name="msb_address",
|
|
||||||
mod=self.msb_address,
|
|
||||||
offset=self.msb_address_position,
|
|
||||||
rotate=270)
|
|
||||||
|
|
||||||
self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)]
|
|
||||||
temp = list(self.msb_bank_sel_addr)
|
|
||||||
temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]])
|
|
||||||
temp.extend(["clk_buf", "vdd", "gnd"])
|
|
||||||
self.connect_inst(temp)
|
|
||||||
|
|
||||||
self.msb_decoder_inst = self.add_inst(name="msb_decoder",
|
|
||||||
mod=self.msb_decoder,
|
|
||||||
offset=self.msb_decoder_position,
|
|
||||||
mirror="MY")
|
|
||||||
temp = ["msb[{}]".format(i) for i in range(2)]
|
|
||||||
temp.extend(["bank_sel[{}]".format(i) for i in range(4)])
|
|
||||||
temp.extend(["vdd", "gnd"])
|
|
||||||
self.connect_inst(temp)
|
|
||||||
|
|
||||||
def route_double_msb_address(self):
|
|
||||||
""" Route two MSB address bits and the bank decoder for 4-bank SRAM """
|
|
||||||
|
|
||||||
# connect the MSB flops to the address input bus
|
|
||||||
for i in [0,1]:
|
|
||||||
msb_pins = self.msb_address_inst.get_pins("din_{}".format(i))
|
|
||||||
for msb_pin in msb_pins:
|
|
||||||
if msb_pin.layer == "metal3":
|
|
||||||
msb_pin_pos = msb_pin.lc()
|
|
||||||
break
|
|
||||||
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y)
|
|
||||||
self.add_path("metal3",[msb_pin_pos,rail_pos])
|
|
||||||
self.add_via_center(("metal2","via2","metal3"),rail_pos)
|
|
||||||
|
|
||||||
# Connect clk
|
|
||||||
clk_pin = self.msb_address_inst.get_pin("clk")
|
|
||||||
clk_pos = clk_pin.bc()
|
|
||||||
rail_pos = self.horz_control_bus_positions["clk_buf"]
|
|
||||||
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
|
|
||||||
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
|
|
||||||
|
|
||||||
# Connect bank decoder outputs to the bank select vertical bus wires
|
|
||||||
for i in range(self.num_banks):
|
|
||||||
msb_pin = self.msb_decoder_inst.get_pin("out_{}".format(i))
|
|
||||||
msb_pin_pos = msb_pin.lc()
|
|
||||||
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y)
|
|
||||||
self.add_path("metal1",[msb_pin_pos,rail_pos])
|
|
||||||
self.add_via_center(("metal1","via1","metal2"),rail_pos)
|
|
||||||
|
|
||||||
# connect MSB flop outputs to the bank decoder inputs
|
|
||||||
msb_pin = self.msb_address_inst.get_pin("dout[0]")
|
|
||||||
msb_pin_pos = msb_pin.rc()
|
|
||||||
in_pin = self.msb_decoder_inst.get_pin("in[0]")
|
|
||||||
in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom
|
|
||||||
out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right
|
|
||||||
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
|
|
||||||
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
|
|
||||||
self.add_via_center(("metal1","via1","metal2"),in_pos)
|
|
||||||
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
|
|
||||||
|
|
||||||
msb_pin = self.msb_address_inst.get_pin("dout[1]")
|
|
||||||
msb_pin_pos = msb_pin.rc()
|
|
||||||
in_pin = self.msb_decoder_inst.get_pin("in[1]")
|
|
||||||
in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up
|
|
||||||
out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right
|
|
||||||
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
|
|
||||||
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
|
|
||||||
self.add_via_center(("metal1","via1","metal2"),in_pos)
|
|
||||||
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
|
|
||||||
|
|
||||||
self.route_double_msb_address_supplies()
|
|
||||||
|
|
||||||
def route_double_msb_address_supplies(self):
|
|
||||||
""" Route the vdd/gnd bits of the 2-bit bank decoder. """
|
|
||||||
|
|
||||||
# Route the right-most vdd/gnd of the right upper bank to the top of the decoder
|
|
||||||
vdd_pins = self.bank_inst[1].get_pins("vdd")
|
|
||||||
left_bank_vdd_pin = None
|
|
||||||
right_bank_vdd_pin = None
|
|
||||||
for vdd_pin in vdd_pins:
|
|
||||||
if vdd_pin.layer != "metal2":
|
|
||||||
continue
|
|
||||||
if left_bank_vdd_pin == None or vdd_pin.lx()<left_bank_vdd_pin.lx():
|
|
||||||
left_bank_vdd_pin = vdd_pin
|
|
||||||
if right_bank_vdd_pin == None or vdd_pin.lx()>right_bank_vdd_pin.lx():
|
|
||||||
right_bank_vdd_pin = vdd_pin
|
|
||||||
# Route to top
|
|
||||||
self.add_rect(layer="metal2",
|
|
||||||
offset=vdd_pin.ul(),
|
|
||||||
height=self.height-vdd_pin.uy(),
|
|
||||||
width=vdd_pin.width())
|
|
||||||
|
|
||||||
gnd_pins = self.bank_inst[1].get_pins("gnd")
|
|
||||||
left_bank_gnd_pin = None
|
|
||||||
right_bank_gnd_pin = None
|
|
||||||
for gnd_pin in gnd_pins:
|
|
||||||
if gnd_pin.layer != "metal2":
|
|
||||||
continue
|
|
||||||
if left_bank_gnd_pin == None or gnd_pin.lx()<left_bank_gnd_pin.lx():
|
|
||||||
left_bank_gnd_pin = gnd_pin
|
|
||||||
if right_bank_gnd_pin == None or gnd_pin.lx()>right_bank_gnd_pin.lx():
|
|
||||||
right_bank_gnd_pin = gnd_pin
|
|
||||||
# Route to top
|
|
||||||
self.add_rect(layer="metal2",
|
|
||||||
offset=gnd_pin.ul(),
|
|
||||||
height=self.height-gnd_pin.uy(),
|
|
||||||
width=gnd_pin.width())
|
|
||||||
|
|
||||||
# Connect bank decoder vdd/gnd supplies using the previous bank pins
|
|
||||||
vdd_pins = self.msb_decoder_inst.get_pins("vdd")
|
|
||||||
for vdd_pin in vdd_pins:
|
|
||||||
if vdd_pin.layer != "metal1":
|
|
||||||
continue
|
|
||||||
rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy())
|
|
||||||
rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy())
|
|
||||||
self.add_path("metal1",[rail1_pos,rail2_pos])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail1_pos,
|
|
||||||
rotate=90,
|
|
||||||
size=[1,3])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail2_pos,
|
|
||||||
rotate=90,
|
|
||||||
size=[1,3])
|
|
||||||
gnd_pins = self.msb_decoder_inst.get_pins("gnd")
|
|
||||||
for gnd_pin in gnd_pins:
|
|
||||||
if gnd_pin.layer != "metal1":
|
|
||||||
continue
|
|
||||||
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
|
|
||||||
rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy())
|
|
||||||
self.add_path("metal1",[rail1_pos,rail2_pos])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail1_pos,
|
|
||||||
rotate=90,
|
|
||||||
size=[1,3])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail2_pos,
|
|
||||||
rotate=90,
|
|
||||||
size=[1,3])
|
|
||||||
|
|
||||||
# connect the bank MSB flop supplies
|
|
||||||
vdd_pins = self.msb_address_inst.get_pins("vdd")
|
|
||||||
# vdd pins go down to the rail
|
|
||||||
for vdd_pin in vdd_pins:
|
|
||||||
if vdd_pin.layer != "metal1":
|
|
||||||
continue
|
|
||||||
vdd_pos = vdd_pin.bc()
|
|
||||||
down_pos = vdd_pos - vector(0,self.m1_pitch)
|
|
||||||
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
|
|
||||||
self.add_path("metal1",[vdd_pos,down_pos])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=down_pos,
|
|
||||||
rotate=90)
|
|
||||||
self.add_path("metal2",[down_pos,rail_pos])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail_pos)
|
|
||||||
# gnd pins go right to the rail
|
|
||||||
gnd_pins = self.msb_address_inst.get_pins("gnd")
|
|
||||||
for gnd_pin in gnd_pins:
|
|
||||||
if gnd_pin.layer != "metal2":
|
|
||||||
continue
|
|
||||||
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
|
|
||||||
self.add_path("metal1",[rail1_pos,gnd_pin.lc()])
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=gnd_pin.lc(),
|
|
||||||
rotate=90)
|
|
||||||
self.add_via_center(layers=("metal1","via1","metal2"),
|
|
||||||
offset=rail1_pos,
|
|
||||||
rotate=90,
|
|
||||||
size=[1,3])
|
|
||||||
|
|
||||||
|
|
||||||
def route(self):
|
|
||||||
""" Route all of the signals for the four bank SRAM. """
|
|
||||||
|
|
||||||
self.route_shared_banks()
|
|
||||||
|
|
||||||
# connect the data output to the data bus
|
|
||||||
for n in self.data_bus_names:
|
|
||||||
for i in [0,1]:
|
|
||||||
pin_pos = self.bank_inst[i].get_pin(n).bc()
|
|
||||||
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
|
|
||||||
self.add_path("metal2",[pin_pos,rail_pos])
|
|
||||||
self.add_via_center(("metal2","via2","metal3"),rail_pos)
|
|
||||||
|
|
||||||
for i in [2,3]:
|
|
||||||
pin_pos = self.bank_inst[i].get_pin(n).uc()
|
|
||||||
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
|
|
||||||
self.add_path("metal2",[pin_pos,rail_pos])
|
|
||||||
self.add_via_center(("metal2","via2","metal3"),rail_pos)
|
|
||||||
|
|
||||||
# route msb address bits
|
|
||||||
# route 2:4 decoder
|
|
||||||
self.route_double_msb_address()
|
|
||||||
|
|
||||||
# connect the banks to the vertical address bus
|
|
||||||
# connect the banks to the vertical control bus
|
|
||||||
for n in self.addr_bus_names + self.control_bus_names:
|
|
||||||
# Skip these from the horizontal bus
|
|
||||||
if n in ["vdd", "gnd"]: continue
|
|
||||||
# This will be the bank select, so skip it
|
|
||||||
if n in self.msb_bank_sel_addr: continue
|
|
||||||
|
|
||||||
for bank_id in [0,2]:
|
|
||||||
pin0_pos = self.bank_inst[bank_id].get_pin(n).rc()
|
|
||||||
pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc()
|
|
||||||
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
|
|
||||||
self.add_path("metal3",[pin0_pos,pin1_pos])
|
|
||||||
self.add_via_center(("metal2","via2","metal3"),rail_pos)
|
|
||||||
|
|
||||||
|
|
||||||
self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3])
|
|
||||||
|
|
||||||
|
|
||||||
def add_lvs_correspondence_points(self):
|
|
||||||
"""
|
|
||||||
This adds some points for easier debugging if LVS goes wrong.
|
|
||||||
These should probably be turned off by default though, since extraction
|
|
||||||
will show these as ports in the extracted netlist.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if self.num_banks==1: return
|
|
||||||
|
|
||||||
for n in self.control_bus_names:
|
|
||||||
self.add_label(text=n,
|
|
||||||
layer="metal2",
|
|
||||||
offset=self.vert_control_bus_positions[n])
|
|
||||||
for n in self.bank_sel_bus_names:
|
|
||||||
self.add_label(text=n,
|
|
||||||
layer="metal2",
|
|
||||||
offset=self.vert_control_bus_positions[n])
|
|
||||||
|
|
@ -2,6 +2,7 @@ import sys
|
||||||
import datetime
|
import datetime
|
||||||
import getpass
|
import getpass
|
||||||
import debug
|
import debug
|
||||||
|
from datetime import datetime
|
||||||
from importlib import reload
|
from importlib import reload
|
||||||
from vector import vector
|
from vector import vector
|
||||||
from globals import OPTS, print_time
|
from globals import OPTS, print_time
|
||||||
|
|
@ -63,37 +64,52 @@ class sram_base(design):
|
||||||
|
|
||||||
def create_netlist(self):
|
def create_netlist(self):
|
||||||
""" Netlist creation """
|
""" Netlist creation """
|
||||||
|
|
||||||
|
start_time = datetime.now()
|
||||||
|
|
||||||
# Must create the control logic before pins to get the pins
|
# Must create the control logic before pins to get the pins
|
||||||
self.add_modules()
|
self.add_modules()
|
||||||
self.add_pins()
|
self.add_pins()
|
||||||
|
self.create_modules()
|
||||||
|
|
||||||
# This is for the lib file if we don't create layout
|
# This is for the lib file if we don't create layout
|
||||||
self.width=0
|
self.width=0
|
||||||
self.height=0
|
self.height=0
|
||||||
|
|
||||||
|
|
||||||
|
if not OPTS.is_unit_test:
|
||||||
|
print_time("Submodules",datetime.now(), start_time)
|
||||||
|
|
||||||
|
|
||||||
def create_layout(self):
|
def create_layout(self):
|
||||||
""" Layout creation """
|
""" Layout creation """
|
||||||
|
start_time = datetime.now()
|
||||||
self.place_instances()
|
self.place_instances()
|
||||||
|
if not OPTS.is_unit_test:
|
||||||
|
print_time("Placement",datetime.now(), start_time)
|
||||||
|
|
||||||
|
start_time = datetime.now()
|
||||||
self.route_layout()
|
self.route_layout()
|
||||||
|
self.route_supplies()
|
||||||
|
if not OPTS.is_unit_test:
|
||||||
|
print_time("Routing",datetime.now(), start_time)
|
||||||
|
|
||||||
self.add_lvs_correspondence_points()
|
self.add_lvs_correspondence_points()
|
||||||
|
|
||||||
self.offset_all_coordinates()
|
self.offset_all_coordinates()
|
||||||
|
|
||||||
# Must be done after offsetting lower-left
|
|
||||||
self.route_supplies()
|
|
||||||
|
|
||||||
highest_coord = self.find_highest_coords()
|
highest_coord = self.find_highest_coords()
|
||||||
self.width = highest_coord[0]
|
self.width = highest_coord[0]
|
||||||
self.height = highest_coord[1]
|
self.height = highest_coord[1]
|
||||||
|
|
||||||
|
start_time = datetime.now()
|
||||||
self.DRC_LVS(final_verification=True)
|
self.DRC_LVS(final_verification=True)
|
||||||
|
if not OPTS.is_unit_test:
|
||||||
|
print_time("Verification",datetime.now(), start_time)
|
||||||
|
|
||||||
|
def create_modules(self):
|
||||||
|
debug.error("Must override pure virtual function.",-1)
|
||||||
|
|
||||||
def route_supplies(self):
|
def route_supplies(self):
|
||||||
""" Route the supply grid and connect the pins to them. """
|
""" Route the supply grid and connect the pins to them. """
|
||||||
|
|
||||||
|
|
@ -140,11 +156,16 @@ class sram_base(design):
|
||||||
# The order of the control signals on the control bus:
|
# The order of the control signals on the control bus:
|
||||||
self.control_bus_names = []
|
self.control_bus_names = []
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)]
|
self.control_bus_names[port] = ["clk_buf{}".format(port)]
|
||||||
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"):
|
wen = "w_en{}".format(port)
|
||||||
self.control_bus_names[port].append("w_en{}".format(port))
|
sen = "s_en{}".format(port)
|
||||||
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"):
|
pen = "p_en_bar{}".format(port)
|
||||||
self.control_bus_names[port].append("s_en{}".format(port))
|
if self.port_id[port] == "r":
|
||||||
|
self.control_bus_names[port].extend([sen, pen])
|
||||||
|
elif self.port_id[port] == "w":
|
||||||
|
self.control_bus_names[port].extend([wen])
|
||||||
|
else:
|
||||||
|
self.control_bus_names[port].extend([sen, wen, pen])
|
||||||
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
|
self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
|
||||||
pitch=self.m2_pitch,
|
pitch=self.m2_pitch,
|
||||||
offset=self.vertical_bus_offset,
|
offset=self.vertical_bus_offset,
|
||||||
|
|
@ -287,11 +308,12 @@ class sram_base(design):
|
||||||
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
|
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
|
||||||
for port in self.read_ports:
|
for port in self.read_ports:
|
||||||
temp.append("s_en{0}".format(port))
|
temp.append("s_en{0}".format(port))
|
||||||
|
for port in self.read_ports:
|
||||||
|
temp.append("p_en_bar{0}".format(port))
|
||||||
for port in self.write_ports:
|
for port in self.write_ports:
|
||||||
temp.append("w_en{0}".format(port))
|
temp.append("w_en{0}".format(port))
|
||||||
for port in self.all_ports:
|
for port in self.all_ports:
|
||||||
temp.append("clk_buf_bar{0}".format(port))
|
temp.append("wl_en{0}".format(port))
|
||||||
temp.append("clk_buf{0}".format(port))
|
|
||||||
temp.extend(["vdd", "gnd"])
|
temp.extend(["vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
|
|
@ -403,16 +425,21 @@ class sram_base(design):
|
||||||
mod = self.control_logic_r
|
mod = self.control_logic_r
|
||||||
|
|
||||||
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
|
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
|
||||||
|
|
||||||
|
# Inputs
|
||||||
temp = ["csb{}".format(port)]
|
temp = ["csb{}".format(port)]
|
||||||
if port in self.readwrite_ports:
|
if port in self.readwrite_ports:
|
||||||
temp.append("web{}".format(port))
|
temp.append("web{}".format(port))
|
||||||
temp.append("clk{}".format(port))
|
temp.append("clk{}".format(port))
|
||||||
|
|
||||||
|
# Ouputs
|
||||||
if port in self.read_ports:
|
if port in self.read_ports:
|
||||||
temp.append("s_en{}".format(port))
|
temp.append("s_en{}".format(port))
|
||||||
if port in self.write_ports:
|
if port in self.write_ports:
|
||||||
temp.append("w_en{}".format(port))
|
temp.append("w_en{}".format(port))
|
||||||
temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
|
if port in self.read_ports:
|
||||||
|
temp.append("p_en_bar{}".format(port))
|
||||||
|
temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
|
||||||
self.connect_inst(temp)
|
self.connect_inst(temp)
|
||||||
|
|
||||||
return insts
|
return insts
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ class sram_config:
|
||||||
# This will get over-written when we determine the organization
|
# This will get over-written when we determine the organization
|
||||||
self.words_per_row = None
|
self.words_per_row = None
|
||||||
|
|
||||||
# Move the module names to this?
|
self.compute_sizes()
|
||||||
|
|
||||||
|
|
||||||
def set_local_config(self, module):
|
def set_local_config(self, module):
|
||||||
|
|
@ -54,6 +54,20 @@ class sram_config:
|
||||||
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
|
self.tentative_num_rows = self.num_bits_per_bank / (self.words_per_row*self.word_size)
|
||||||
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
|
self.words_per_row = self.amend_words_per_row(self.tentative_num_rows, self.words_per_row)
|
||||||
|
|
||||||
|
debug.info(1,"Words per row: {}".format(self.words_per_row))
|
||||||
|
self.recompute_sizes()
|
||||||
|
|
||||||
|
def recompute_sizes(self):
|
||||||
|
"""
|
||||||
|
Calculate the auxiliary values assuming fixed number of words per row.
|
||||||
|
This can be called multiple times from the unit test when we reconfigure an
|
||||||
|
SRAM for testing.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# If the banks changed
|
||||||
|
self.num_words_per_bank = self.num_words/self.num_banks
|
||||||
|
self.num_bits_per_bank = self.word_size*self.num_words_per_bank
|
||||||
|
|
||||||
# Fix the number of columns and rows
|
# Fix the number of columns and rows
|
||||||
self.num_cols = int(self.words_per_row*self.word_size)
|
self.num_cols = int(self.words_per_row*self.word_size)
|
||||||
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
|
self.num_rows = int(self.num_words_per_bank/self.words_per_row)
|
||||||
|
|
@ -64,7 +78,6 @@ class sram_config:
|
||||||
self.bank_addr_size = self.col_addr_size + self.row_addr_size
|
self.bank_addr_size = self.col_addr_size + self.row_addr_size
|
||||||
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
|
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
|
||||||
|
|
||||||
debug.info(1,"Words per row: {}".format(self.words_per_row))
|
|
||||||
|
|
||||||
def estimate_words_per_row(self,tentative_num_cols, word_size):
|
def estimate_words_per_row(self,tentative_num_cols, word_size):
|
||||||
"""
|
"""
|
||||||
|
|
@ -74,10 +87,14 @@ class sram_config:
|
||||||
|
|
||||||
if tentative_num_cols < 1.5*word_size:
|
if tentative_num_cols < 1.5*word_size:
|
||||||
return 1
|
return 1
|
||||||
elif tentative_num_cols > 3*word_size:
|
elif tentative_num_cols < 3*word_size:
|
||||||
|
return 2
|
||||||
|
elif tentative_num_cols < 6*word_size:
|
||||||
return 4
|
return 4
|
||||||
else:
|
else:
|
||||||
return 2
|
if tentative_num_cols > 16*word_size:
|
||||||
|
debug.warning("Reaching column mux size limit. Consider increasing above 8-way.")
|
||||||
|
return 8
|
||||||
|
|
||||||
def amend_words_per_row(self,tentative_num_rows, words_per_row):
|
def amend_words_per_row(self,tentative_num_rows, words_per_row):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Run a regression test on a dff_inv.
|
Run a regression test on a pand2 cell
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
@ -11,19 +11,22 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
class dff_inv_test(openram_test):
|
class pand2_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
import dff_inv
|
global verify
|
||||||
|
import verify
|
||||||
|
|
||||||
debug.info(2, "Testing dff_inv 4x")
|
import pand2
|
||||||
a = dff_inv.dff_inv(4)
|
|
||||||
|
debug.info(2, "Testing pand2 gate 4x")
|
||||||
|
a = pand2.pand2(4)
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
globals.end_openram()
|
globals.end_openram()
|
||||||
|
|
||||||
# run the test from the command line
|
# instantiate a copdsay of the class to actually run the test
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
(OPTS, args) = globals.parse_args()
|
(OPTS, args) = globals.parse_args()
|
||||||
del sys.argv[1:]
|
del sys.argv[1:]
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Run a regression test on a dff_array.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
from globals import OPTS
|
|
||||||
import debug
|
|
||||||
|
|
||||||
class dff_inv_array_test(openram_test):
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
|
||||||
import dff_inv_array
|
|
||||||
|
|
||||||
debug.info(2, "Testing dff_inv_array for 3x3")
|
|
||||||
a = dff_inv_array.dff_inv_array(rows=3, columns=3)
|
|
||||||
self.local_check(a)
|
|
||||||
|
|
||||||
debug.info(2, "Testing dff_inv_array for 1x3")
|
|
||||||
a = dff_inv_array.dff_inv_array(rows=1, columns=3)
|
|
||||||
self.local_check(a)
|
|
||||||
|
|
||||||
debug.info(2, "Testing dff_inv_array for 3x1")
|
|
||||||
a = dff_inv_array.dff_inv_array(rows=3, columns=1)
|
|
||||||
self.local_check(a)
|
|
||||||
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
# run the test from the command line
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -24,18 +24,21 @@ class multi_bank_test(openram_test):
|
||||||
c.num_banks=2
|
c.num_banks=2
|
||||||
|
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "No column mux")
|
debug.info(1, "No column mux")
|
||||||
a = bank(c, name="bank1_multi")
|
a = bank(c, name="bank1_multi")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
c.num_words=32
|
c.num_words=32
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Two way column mux")
|
debug.info(1, "Two way column mux")
|
||||||
a = bank(c, name="bank2_multi")
|
a = bank(c, name="bank2_multi")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
||||||
c.num_words=64
|
c.num_words=64
|
||||||
c.words_per_row=4
|
c.words_per_row=4
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Four way column mux")
|
debug.info(1, "Four way column mux")
|
||||||
a = bank(c, name="bank3_multi")
|
a = bank(c, name="bank3_multi")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
@ -43,6 +46,7 @@ class multi_bank_test(openram_test):
|
||||||
c.word_size=2
|
c.word_size=2
|
||||||
c.num_words=128
|
c.num_words=128
|
||||||
c.words_per_row=8
|
c.words_per_row=8
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Eight way column mux")
|
debug.info(1, "Eight way column mux")
|
||||||
a = bank(c, name="bank4_multi")
|
a = bank(c, name="bank4_multi")
|
||||||
self.local_check(a)
|
self.local_check(a)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ class psingle_bank_test(openram_test):
|
||||||
num_words=16)
|
num_words=16)
|
||||||
|
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "No column mux")
|
debug.info(1, "No column mux")
|
||||||
name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
||||||
a = bank(c, name=name)
|
a = bank(c, name=name)
|
||||||
|
|
@ -38,6 +39,7 @@ class psingle_bank_test(openram_test):
|
||||||
|
|
||||||
c.num_words=32
|
c.num_words=32
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Two way column mux")
|
debug.info(1, "Two way column mux")
|
||||||
name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
||||||
a = bank(c, name=name)
|
a = bank(c, name=name)
|
||||||
|
|
@ -45,6 +47,7 @@ class psingle_bank_test(openram_test):
|
||||||
|
|
||||||
c.num_words=64
|
c.num_words=64
|
||||||
c.words_per_row=4
|
c.words_per_row=4
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Four way column mux")
|
debug.info(1, "Four way column mux")
|
||||||
name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
||||||
a = bank(c, name=name)
|
a = bank(c, name=name)
|
||||||
|
|
@ -53,6 +56,7 @@ class psingle_bank_test(openram_test):
|
||||||
c.word_size=2
|
c.word_size=2
|
||||||
c.num_words=128
|
c.num_words=128
|
||||||
c.words_per_row=8
|
c.words_per_row=8
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Four way column mux")
|
debug.info(1, "Four way column mux")
|
||||||
name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
|
||||||
a = bank(c, name=name)
|
a = bank(c, name=name)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Run a regression test on a 1 bank SRAM
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header,openram_test
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
import debug
|
||||||
|
|
||||||
|
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
|
||||||
|
class psram_1bank_2mux_1rw_1w_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
from sram import sram
|
||||||
|
from sram_config import sram_config
|
||||||
|
OPTS.bitcell = "pbitcell"
|
||||||
|
OPTS.replica_bitcell="replica_pbitcell"
|
||||||
|
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_w_ports = 1
|
||||||
|
OPTS.num_r_ports = 0
|
||||||
|
|
||||||
|
c = sram_config(word_size=4,
|
||||||
|
num_words=32,
|
||||||
|
num_banks=1)
|
||||||
|
c.num_words=32
|
||||||
|
c.words_per_row=2
|
||||||
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
|
a = sram(c, "sram")
|
||||||
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
|
|
@ -11,8 +11,8 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
|
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
|
||||||
class psram_1bank_2mux_test(openram_test):
|
class psram_1bank_2mux_1w_1r_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
|
@ -30,7 +30,14 @@ class psram_1bank_2mux_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.num_words=32
|
c.num_words=32
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
debug.info(1, "Single bank two way column mux 1w/1r with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
|
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_test, wide metal supply routing error")
|
||||||
class psram_1bank_2mux_test(openram_test):
|
class psram_1bank_2mux_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
@ -31,7 +31,14 @@ class psram_1bank_2mux_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.num_words=32
|
c.num_words=32
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
debug.info(1, "Single bank two way column mux with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Run a regression test on a 1 bank SRAM
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header,openram_test
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class psram_1bank_4mux_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
from sram import sram
|
||||||
|
from sram_config import sram_config
|
||||||
|
OPTS.bitcell = "pbitcell"
|
||||||
|
OPTS.replica_bitcell="replica_pbitcell"
|
||||||
|
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
|
||||||
|
c = sram_config(word_size=4,
|
||||||
|
num_words=64,
|
||||||
|
num_banks=1)
|
||||||
|
c.num_words=64
|
||||||
|
c.words_per_row=4
|
||||||
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
|
a = sram(c, "sram")
|
||||||
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
|
|
@ -29,7 +29,14 @@ class sram_1bank_2mux_1rw_1r_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
|
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
debug.info(1, "Single bank, two way column mux 1rw, 1r with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,14 @@ class sram_1bank_2mux_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
|
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
debug.info(1, "Single bank two way column mux with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,14 @@ class sram_1bank_4mux_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
|
|
||||||
c.words_per_row=4
|
c.words_per_row=4
|
||||||
debug.info(1, "Single bank, four way column mux with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Run a regression test on a 1 bank SRAM
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from testutils import header,openram_test
|
||||||
|
import sys,os
|
||||||
|
sys.path.append(os.path.join(sys.path[0],".."))
|
||||||
|
import globals
|
||||||
|
from globals import OPTS
|
||||||
|
import debug
|
||||||
|
|
||||||
|
class sram_1bank_8mux_1rw_1r_test(openram_test):
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
from sram import sram
|
||||||
|
from sram_config import sram_config
|
||||||
|
|
||||||
|
OPTS.bitcell = "bitcell_1rw_1r"
|
||||||
|
OPTS.replica_bitcell = "replica_bitcell_1rw_1r"
|
||||||
|
OPTS.num_rw_ports = 1
|
||||||
|
OPTS.num_r_ports = 1
|
||||||
|
OPTS.num_w_ports = 0
|
||||||
|
|
||||||
|
c = sram_config(word_size=2,
|
||||||
|
num_words=128,
|
||||||
|
num_banks=1)
|
||||||
|
|
||||||
|
c.words_per_row=8
|
||||||
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
|
a = sram(c, "sram")
|
||||||
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
globals.end_openram()
|
||||||
|
|
||||||
|
# run the test from the command line
|
||||||
|
if __name__ == "__main__":
|
||||||
|
(OPTS, args) = globals.parse_args()
|
||||||
|
del sys.argv[1:]
|
||||||
|
header(__file__, OPTS.tech_name)
|
||||||
|
unittest.main()
|
||||||
|
|
@ -23,7 +23,14 @@ class sram_1bank_8mux_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
|
|
||||||
c.words_per_row=8
|
c.words_per_row=8
|
||||||
debug.info(1, "Single bank, eight way column mux with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,14 @@ class sram_1bank_nomux_1rw_1r_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
|
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
debug.info(1, "Single bank, no column mux 1rw, 1r with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,14 @@ class sram_1bank_nomux_test(openram_test):
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
|
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
debug.info(1, "Single bank, no column mux with control logic")
|
c.recompute_sizes()
|
||||||
|
debug.info(1, "Layout test for {}rw,{}r,{}w sram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
|
OPTS.num_r_ports,
|
||||||
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
a = sram(c, "sram")
|
a = sram(c, "sram")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,18 +23,21 @@ class sram_2bank_test(openram_test):
|
||||||
num_banks=2)
|
num_banks=2)
|
||||||
|
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Two bank, no column mux with control logic")
|
debug.info(1, "Two bank, no column mux with control logic")
|
||||||
a = sram(c, "sram1")
|
a = sram(c, "sram1")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
c.num_words=64
|
c.num_words=64
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Two bank two way column mux with control logic")
|
debug.info(1, "Two bank two way column mux with control logic")
|
||||||
a = sram(c, "sram2")
|
a = sram(c, "sram2")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
||||||
c.num_words=128
|
c.num_words=128
|
||||||
c.words_per_row=4
|
c.words_per_row=4
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Two bank, four way column mux with control logic")
|
debug.info(1, "Two bank, four way column mux with control logic")
|
||||||
a = sram(c, "sram3")
|
a = sram(c, "sram3")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
@ -42,6 +45,7 @@ class sram_2bank_test(openram_test):
|
||||||
c.word_size=2
|
c.word_size=2
|
||||||
c.num_words=256
|
c.num_words=256
|
||||||
c.words_per_row=8
|
c.words_per_row=8
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Two bank, eight way column mux with control logic")
|
debug.info(1, "Two bank, eight way column mux with control logic")
|
||||||
a = sram(c, "sram4")
|
a = sram(c, "sram4")
|
||||||
self.local_check(a, final_verification=True)
|
self.local_check(a, final_verification=True)
|
||||||
|
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
Run a regression test on a 4 bank SRAM
|
|
||||||
"""
|
|
||||||
|
|
||||||
import unittest
|
|
||||||
from testutils import header,openram_test
|
|
||||||
import sys,os
|
|
||||||
sys.path.append(os.path.join(sys.path[0],".."))
|
|
||||||
import globals
|
|
||||||
from globals import OPTS
|
|
||||||
import debug
|
|
||||||
|
|
||||||
@unittest.skip("Multibank is not working yet.")
|
|
||||||
class sram_4bank_test(openram_test):
|
|
||||||
|
|
||||||
def runTest(self):
|
|
||||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
|
||||||
from sram import sram
|
|
||||||
from sram_config import sram_config
|
|
||||||
c = sram_config(word_size=16,
|
|
||||||
num_words=64,
|
|
||||||
num_banks=4)
|
|
||||||
|
|
||||||
debug.info(1, "Four bank, no column mux with control logic")
|
|
||||||
a = sram(c, "sram1")
|
|
||||||
self.local_check(a, final_verification=True)
|
|
||||||
|
|
||||||
c.num_words=128
|
|
||||||
c.words_per_row=2
|
|
||||||
debug.info(1, "Four bank two way column mux with control logic")
|
|
||||||
a = sram(c, "sram2")
|
|
||||||
self.local_check(a, final_verification=True)
|
|
||||||
|
|
||||||
c.num_words=256
|
|
||||||
c.words_per_row=4
|
|
||||||
debug.info(1, "Four bank, four way column mux with control logic")
|
|
||||||
a = sram(c, "sram3")
|
|
||||||
self.local_check(a, final_verification=True)
|
|
||||||
|
|
||||||
c.word_size=2
|
|
||||||
c.num_words=256
|
|
||||||
c.words_per_row=8
|
|
||||||
debug.info(1, "Four bank, eight way column mux with control logic")
|
|
||||||
a = sram.sram(c, "sram4")
|
|
||||||
self.local_check(a, final_verification=True)
|
|
||||||
|
|
||||||
globals.end_openram()
|
|
||||||
|
|
||||||
# run the test from the command line
|
|
||||||
if __name__ == "__main__":
|
|
||||||
(OPTS, args) = globals.parse_args()
|
|
||||||
del sys.argv[1:]
|
|
||||||
header(__file__, OPTS.tech_name)
|
|
||||||
unittest.main()
|
|
||||||
|
|
@ -30,6 +30,7 @@ class timing_sram_test(openram_test):
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||||
s = sram(c, name="sram1")
|
s = sram(c, name="sram1")
|
||||||
|
|
||||||
|
|
@ -49,29 +50,28 @@ class timing_sram_test(openram_test):
|
||||||
#Combine info about port into all data
|
#Combine info about port into all data
|
||||||
data.update(port_data[0])
|
data.update(port_data[0])
|
||||||
|
|
||||||
#Assumes single rw port (6t sram)
|
|
||||||
if OPTS.tech_name == "freepdk45":
|
if OPTS.tech_name == "freepdk45":
|
||||||
golden_data = {'delay_hl': [2.5829000000000004],
|
golden_data = {'delay_hl': [2.6232],
|
||||||
'delay_lh': [0.2255964],
|
'delay_lh': [0.2775342],
|
||||||
'leakage_power': 0.0019498999999999996,
|
'leakage_power': 0.0020258999999999997,
|
||||||
'min_period': 4.844,
|
'min_period': 4.844,
|
||||||
'read0_power': [0.055371399999999994],
|
'read0_power': [0.0557804],
|
||||||
'read1_power': [0.0520225],
|
'read1_power': [0.0525619],
|
||||||
'slew_hl': [0.0794261],
|
'slew_hl': [0.1082014],
|
||||||
'slew_lh': [0.0236264],
|
'slew_lh': [0.0238257],
|
||||||
'write0_power': [0.06545659999999999],
|
'write0_power': [0.0456528],
|
||||||
'write1_power': [0.057846299999999996]}
|
'write1_power': [0.0442747]}
|
||||||
elif OPTS.tech_name == "scn4m_subm":
|
elif OPTS.tech_name == "scn4m_subm":
|
||||||
golden_data = {'delay_hl': [3.452],
|
golden_data = {'delay_hl': [6.079300000000001],
|
||||||
'delay_lh': [1.3792000000000002],
|
'delay_lh': [1.7767000000000002],
|
||||||
'leakage_power': 0.0257065,
|
'leakage_power': 0.026282499999999997,
|
||||||
'min_period': 4.688,
|
'min_period': 9.375,
|
||||||
'read0_power': [15.0755],
|
'read0_power': [6.5802],
|
||||||
'read1_power': [14.4526],
|
'read1_power': [6.2815],
|
||||||
'slew_hl': [0.6137363],
|
'slew_hl': [0.7396921999999999],
|
||||||
'slew_lh': [0.3381045],
|
'slew_lh': [0.3397355],
|
||||||
'write0_power': [16.9203],
|
'write0_power': [5.7337],
|
||||||
'write1_power': [15.367]}
|
'write1_power': [5.8691]}
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False) # other techs fail
|
self.assertTrue(False) # other techs fail
|
||||||
# Check if no too many or too few results
|
# Check if no too many or too few results
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class timing_sram_test(openram_test):
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
||||||
s = sram(c, name="sram1")
|
s = sram(c, name="sram1")
|
||||||
|
|
||||||
|
|
@ -50,27 +51,27 @@ class timing_sram_test(openram_test):
|
||||||
data.update(port_data[0])
|
data.update(port_data[0])
|
||||||
|
|
||||||
if OPTS.tech_name == "freepdk45":
|
if OPTS.tech_name == "freepdk45":
|
||||||
golden_data = {'delay_hl': [2.584251],
|
golden_data = {'delay_hl': [2.625351],
|
||||||
'delay_lh': [0.22870469999999998],
|
'delay_lh': [0.28080869999999997],
|
||||||
'leakage_power': 0.0009567935,
|
'leakage_power': 0.001040682,
|
||||||
'min_period': 4.844,
|
'min_period': 4.844,
|
||||||
'read0_power': [0.0547588],
|
'read0_power': [0.0553667],
|
||||||
'read1_power': [0.051159970000000006],
|
'read1_power': [0.05177618],
|
||||||
'slew_hl': [0.08164099999999999],
|
'slew_hl': [0.1099853],
|
||||||
'slew_lh': [0.025474979999999998],
|
'slew_lh': [0.02568626],
|
||||||
'write0_power': [0.06513271999999999],
|
'write0_power': [0.04517803],
|
||||||
'write1_power': [0.058057000000000004]}
|
'write1_power': [0.04449207]}
|
||||||
elif OPTS.tech_name == "scn4m_subm":
|
elif OPTS.tech_name == "scn4m_subm":
|
||||||
golden_data = {'delay_hl': [3.644147],
|
golden_data = {'delay_hl': [6.45408],
|
||||||
'delay_lh': [1.629815],
|
'delay_lh': [2.0787519999999997],
|
||||||
'leakage_power': 0.001542964,
|
'leakage_power': 0.001177846,
|
||||||
'min_period': 4.688,
|
'min_period': 9.688,
|
||||||
'read0_power': [16.28732],
|
'read0_power': [7.088419],
|
||||||
'read1_power': [15.75155],
|
'read1_power': [6.824107000000001],
|
||||||
'slew_hl': [0.6722473],
|
'slew_hl': [0.7980976999999999],
|
||||||
'slew_lh': [0.3386347],
|
'slew_lh': [0.3393389],
|
||||||
'write0_power': [18.545450000000002],
|
'write0_power': [5.982207],
|
||||||
'write1_power': [16.81084]}
|
'write1_power': [6.28866]}
|
||||||
else:
|
else:
|
||||||
self.assertTrue(False) # other techs fail
|
self.assertTrue(False) # other techs fail
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
#@unittest.skip("SKIPPING 22_psram_1bank_2mux_func_test")
|
@unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?")
|
||||||
class psram_1bank_2mux_func_test(openram_test):
|
class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
|
||||||
|
|
@ -35,10 +35,14 @@ class psram_1bank_2mux_func_test(openram_test):
|
||||||
num_words=64,
|
num_words=64,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
c.recompute_sizes()
|
||||||
c.num_words,
|
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
c.words_per_row,
|
OPTS.num_r_ports,
|
||||||
c.num_banks))
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
s = sram(c, name="sram")
|
s = sram(c, name="sram")
|
||||||
tempspice = OPTS.openram_temp + "temp.sp"
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
s.sp_write(tempspice)
|
s.sp_write(tempspice)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import globals
|
||||||
from globals import OPTS
|
from globals import OPTS
|
||||||
import debug
|
import debug
|
||||||
|
|
||||||
#@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test")
|
@unittest.skip("SKIPPING 22_psram_1bank_4mux_func_test, third port reads are broken?")
|
||||||
class psram_1bank_4mux_func_test(openram_test):
|
class psram_1bank_4mux_func_test(openram_test):
|
||||||
|
|
||||||
def runTest(self):
|
def runTest(self):
|
||||||
|
|
@ -35,10 +35,14 @@ class psram_1bank_4mux_func_test(openram_test):
|
||||||
num_words=256,
|
num_words=256,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=4
|
c.words_per_row=4
|
||||||
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
c.recompute_sizes()
|
||||||
c.num_words,
|
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
c.words_per_row,
|
OPTS.num_r_ports,
|
||||||
c.num_banks))
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
s = sram(c, name="sram")
|
s = sram(c, name="sram")
|
||||||
tempspice = OPTS.openram_temp + "temp.sp"
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
s.sp_write(tempspice)
|
s.sp_write(tempspice)
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,14 @@ class psram_1bank_8mux_func_test(openram_test):
|
||||||
num_words=256,
|
num_words=256,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=8
|
c.words_per_row=8
|
||||||
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
c.recompute_sizes()
|
||||||
c.num_words,
|
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
c.words_per_row,
|
OPTS.num_r_ports,
|
||||||
c.num_banks))
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
s = sram(c, name="sram")
|
s = sram(c, name="sram")
|
||||||
tempspice = OPTS.openram_temp + "temp.sp"
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
s.sp_write(tempspice)
|
s.sp_write(tempspice)
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,14 @@ class psram_1bank_nomux_func_test(openram_test):
|
||||||
num_words=32,
|
num_words=32,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
debug.info(1, "Functional test for psram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
c.recompute_sizes()
|
||||||
c.num_words,
|
debug.info(1, "Functional test for {}rw,{}r,{}w psram with {} bit words, {} words, {} words per row, {} banks".format(OPTS.num_rw_ports,
|
||||||
c.words_per_row,
|
OPTS.num_r_ports,
|
||||||
c.num_banks))
|
OPTS.num_w_ports,
|
||||||
|
c.word_size,
|
||||||
|
c.num_words,
|
||||||
|
c.words_per_row,
|
||||||
|
c.num_banks))
|
||||||
s = sram(c, name="sram")
|
s = sram(c, name="sram")
|
||||||
tempspice = OPTS.openram_temp + "temp.sp"
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
s.sp_write(tempspice)
|
s.sp_write(tempspice)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class sram_1bank_2mux_func_test(openram_test):
|
||||||
num_words=64,
|
num_words=64,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=2
|
c.words_per_row=2
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
||||||
c.num_words,
|
c.num_words,
|
||||||
c.words_per_row,
|
c.words_per_row,
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class sram_1bank_4mux_func_test(openram_test):
|
||||||
num_words=256,
|
num_words=256,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=4
|
c.words_per_row=4
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
||||||
c.num_words,
|
c.num_words,
|
||||||
c.words_per_row,
|
c.words_per_row,
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class sram_1bank_8mux_func_test(openram_test):
|
||||||
num_words=256,
|
num_words=256,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=8
|
c.words_per_row=8
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
||||||
c.num_words,
|
c.num_words,
|
||||||
c.words_per_row,
|
c.words_per_row,
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ class sram_1bank_nomux_func_test(openram_test):
|
||||||
num_words=32,
|
num_words=32,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
debug.info(1, "Functional test for sram with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
||||||
c.num_words,
|
c.num_words,
|
||||||
c.words_per_row,
|
c.words_per_row,
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ class psram_1bank_nomux_func_test(openram_test):
|
||||||
num_words=32,
|
num_words=32,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
debug.info(1, "Functional test for sram 1rw,1r with {} bit words, {} words, {} words per row, {} banks".format(c.word_size,
|
||||||
c.num_words,
|
c.num_words,
|
||||||
c.words_per_row,
|
c.words_per_row,
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class lib_test(openram_test):
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank")
|
debug.info(1, "Testing analytical timing for sample 2 bit, 16 words SRAM with 1 bank")
|
||||||
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||||
tempspice = OPTS.openram_temp + "temp.sp"
|
tempspice = OPTS.openram_temp + "temp.sp"
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ class lib_test(openram_test):
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank")
|
debug.info(1, "Testing pruned timing for sample 2 bit, 16 words SRAM with 1 bank")
|
||||||
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ class lib_test(openram_test):
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank")
|
debug.info(1, "Testing timing for sample 2 bit, 16 words SRAM with 1 bank")
|
||||||
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ class lef_test(openram_test):
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank")
|
debug.info(1, "Testing LEF for sample 2 bit, 16 words SRAM with 1 bank")
|
||||||
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ class verilog_test(openram_test):
|
||||||
num_words=16,
|
num_words=16,
|
||||||
num_banks=1)
|
num_banks=1)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank")
|
debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 1 bank")
|
||||||
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ class worst_case_timing_sram_test(openram_test):
|
||||||
num_words=num_words,
|
num_words=num_words,
|
||||||
num_banks=num_banks)
|
num_banks=num_banks)
|
||||||
c.words_per_row=1
|
c.words_per_row=1
|
||||||
#c.compute_sizes()
|
c.recompute_sizes()
|
||||||
debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format(
|
debug.info(1, "Testing the timing different bitecells inside a {}bit, {} words SRAM with {} bank".format(
|
||||||
word_size, num_words, num_banks))
|
word_size, num_words, num_banks))
|
||||||
s = sram(c, name="sram1")
|
s = sram(c, name="sram1")
|
||||||
|
|
|
||||||
|
|
@ -42,13 +42,14 @@ class openram_test(openram_test):
|
||||||
|
|
||||||
|
|
||||||
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
|
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
|
||||||
|
# Always perform code coverage
|
||||||
cmd = "python3 {0}/openram.py -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(OPENRAM_HOME,
|
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
|
||||||
out_file,
|
cmd = "{0} -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(exe_name,
|
||||||
out_path,
|
out_file,
|
||||||
verbosity,
|
out_path,
|
||||||
OPTS.tech_name,
|
verbosity,
|
||||||
out_path)
|
OPTS.tech_name,
|
||||||
|
out_path)
|
||||||
debug.info(1, cmd)
|
debug.info(1, cmd)
|
||||||
os.system(cmd)
|
os.system(cmd)
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -2,34 +2,35 @@
|
||||||
.SUBCKT write_driver din bl br en vdd gnd
|
.SUBCKT write_driver din bl br en vdd gnd
|
||||||
|
|
||||||
**** Inverter to conver Data_in to data_in_bar ******
|
**** Inverter to conver Data_in to data_in_bar ******
|
||||||
M_1 din_bar din gnd gnd n W='1.2*1u' L=0.6u
|
* din_bar = inv(din)
|
||||||
M_2 din_bar din vdd vdd p W='2.1*1u' L=0.6u
|
M_1 din_bar din gnd gnd n W=1.2u L=0.6u
|
||||||
|
M_2 din_bar din vdd vdd p W=2.1u L=0.6u
|
||||||
|
|
||||||
**** 2input nand gate follwed by inverter to drive BL ******
|
**** 2input nand gate follwed by inverter to drive BL ******
|
||||||
M_3 din_bar_gated en net_7 gnd n W='2.1*1u' L=0.6u
|
* din_bar_gated = nand(en, din)
|
||||||
M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u
|
M_3 din_bar_gated en net_7 gnd n W=2.1u L=0.6u
|
||||||
M_5 din_bar_gated en vdd vdd p W='2.1*1u' L=0.6u
|
M_4 net_7 din gnd gnd n W=2.1u L=0.6u
|
||||||
M_6 din_bar_gated din vdd vdd p W='2.1*1u' L=0.6u
|
M_5 din_bar_gated en vdd vdd p W=2.1u L=0.6u
|
||||||
|
M_6 din_bar_gated din vdd vdd p W=2.1u L=0.6u
|
||||||
|
* din_bar_gated_bar = inv(din_bar_gated)
|
||||||
M_7 net_1 din_bar_gated vdd vdd p W='2.1*1u' L=0.6u
|
M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=2.1u L=0.6u
|
||||||
M_8 net_1 din_bar_gated gnd gnd n W='1.2*1u' L=0.6u
|
M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=1.2u L=0.6u
|
||||||
|
|
||||||
**** 2input nand gate follwed by inverter to drive BR******
|
**** 2input nand gate follwed by inverter to drive BR******
|
||||||
|
* din_gated = nand(en, din_bar)
|
||||||
M_9 din_gated en vdd vdd p W='2.1*1u' L=0.6u
|
M_9 din_gated en vdd vdd p W=2.1u L=0.6u
|
||||||
M_10 din_gated en net_8 gnd n W='2.1*1u' L=0.6u
|
M_10 din_gated en net_8 gnd n W=2.1u L=0.6u
|
||||||
M_11 net_8 din_bar gnd gnd n W='2.1*1u' L=0.6u
|
M_11 net_8 din_bar gnd gnd n W=2.1u L=0.6u
|
||||||
M_12 din_gated din_bar vdd vdd p W='2.1*1u' L=0.6u
|
M_12 din_gated din_bar vdd vdd p W=2.1u L=0.6u
|
||||||
|
* din_gated_bar = inv(din_gated)
|
||||||
M_13 net_6 din_gated vdd vdd p W='2.1*1u' L=0.6u
|
M_13 din_gated_bar din_gated vdd vdd p W=2.1u L=0.6u
|
||||||
M_14 net_6 din_gated gnd gnd n W='1.2*1u' L=0.6u
|
M_14 din_gated_bar din_gated gnd gnd n W=1.2u L=0.6u
|
||||||
|
|
||||||
************************************************
|
************************************************
|
||||||
|
* pull down with en enable
|
||||||
M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u
|
M_15 bl din_gated_bar net_5 gnd n W=3.6u L=0.6u
|
||||||
M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u
|
M_16 br din_bar_gated_bar net_5 gnd n W=3.6u L=0.6u
|
||||||
M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u
|
M_17 net_5 en gnd gnd n W=3.6u L=0.6u
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,37 @@
|
||||||
*********************** Write_Driver ******************************
|
*********************** Write_Driver ******************************
|
||||||
.SUBCKT write_driver din bl br en vdd gnd
|
.SUBCKT write_driver din bl br en vdd gnd
|
||||||
* SPICE3 file created from write_driver.ext - technology: scmos
|
|
||||||
|
|
||||||
M1000 a_44_708# a_36_700# bl gnd n w=2.4u l=0.4u
|
**** Inverter to conver Data_in to data_in_bar ******
|
||||||
M1001 br a_16_500# a_44_708# gnd n w=2.4u l=0.4u
|
* din_bar = inv(din)
|
||||||
M1002 a_44_708# en gnd gnd n w=2.4u l=0.4u
|
M_1 din_bar din gnd gnd n W=0.8u L=0.4u
|
||||||
M1003 gnd a_8_284# a_16_500# gnd n w=0.8u l=0.4u
|
M_2 din_bar din vdd vdd p W=1.4u L=0.4u
|
||||||
M1004 a_36_700# a_20_328# gnd gnd n w=0.8u l=0.4u
|
|
||||||
M1005 vdd a_8_284# a_16_500# vdd p w=1.4u l=0.4u
|
|
||||||
M1006 a_36_700# a_20_328# vdd vdd p w=1.4u l=0.4u
|
|
||||||
M1007 vdd en a_20_328# vdd p w=1.4u l=0.4u
|
|
||||||
M1008 a_20_328# a_64_360# vdd vdd p w=1.4u l=0.4u
|
|
||||||
M1009 a_48_328# en a_20_328# gnd n w=1.4u l=0.4u
|
|
||||||
M1010 gnd a_64_360# a_48_328# gnd n w=1.4u l=0.4u
|
|
||||||
M1011 a_40_228# en a_8_284# gnd n w=1.4u l=0.4u
|
|
||||||
M1012 gnd din a_40_228# gnd n w=1.4u l=0.4u
|
|
||||||
M1013 a_64_360# din gnd gnd n w=0.8u l=0.4u
|
|
||||||
M1014 a_8_284# en vdd vdd p w=1.4u l=0.4u
|
|
||||||
M1015 vdd din a_8_284# vdd p w=1.4u l=0.4u
|
|
||||||
M1016 a_64_360# din vdd vdd p w=1.4u l=0.4u
|
|
||||||
|
|
||||||
.ENDS
|
**** 2input nand gate follwed by inverter to drive BL ******
|
||||||
|
* din_bar_gated = nand(en, din)
|
||||||
|
M_3 din_bar_gated en net_7 gnd n W=1.4u L=0.4u
|
||||||
|
M_4 net_7 din gnd gnd n W=1.4u L=0.4u
|
||||||
|
M_5 din_bar_gated en vdd vdd p W=1.4u L=0.4u
|
||||||
|
M_6 din_bar_gated din vdd vdd p W=1.4u L=0.4u
|
||||||
|
* din_bar_gated_bar = inv(din_bar_gated)
|
||||||
|
M_7 din_bar_gated_bar din_bar_gated vdd vdd p W=1.4u L=0.4u
|
||||||
|
M_8 din_bar_gated_bar din_bar_gated gnd gnd n W=0.8u L=0.4u
|
||||||
|
|
||||||
|
**** 2input nand gate follwed by inverter to drive BR******
|
||||||
|
* din_gated = nand(en, din_bar)
|
||||||
|
M_9 din_gated en vdd vdd p W=1.4u L=0.4u
|
||||||
|
M_10 din_gated en net_8 gnd n W=1.4u L=0.4u
|
||||||
|
M_11 net_8 din_bar gnd gnd n W=1.4u L=0.4u
|
||||||
|
M_12 din_gated din_bar vdd vdd p W=1.4u L=0.4u
|
||||||
|
* din_gated_bar = inv(din_gated)
|
||||||
|
M_13 din_gated_bar din_gated vdd vdd p W=1.4u L=0.4u
|
||||||
|
M_14 din_gated_bar din_gated gnd gnd n W=0.8u L=0.4u
|
||||||
|
|
||||||
|
************************************************
|
||||||
|
* pull down with en enable
|
||||||
|
M_15 bl din_gated_bar net_5 gnd n W=2.4u L=0.4u
|
||||||
|
M_16 br din_bar_gated_bar net_5 gnd n W=2.4u L=0.4u
|
||||||
|
M_17 net_5 en gnd gnd n W=2.4u L=0.4u
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.ENDS $ write_driver
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"
|
||||||
|
|
||||||
|
|
||||||
#spice stimulus related variables
|
#spice stimulus related variables
|
||||||
spice["feasible_period"] = 5 # estimated feasible period in ns
|
spice["feasible_period"] = 10 # estimated feasible period in ns
|
||||||
spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts]
|
spice["supply_voltages"] = [4.5, 5.0, 5.5] # Supply voltage corners in [Volts]
|
||||||
spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts]
|
spice["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts]
|
||||||
spice["rise_time"] = 0.05 # rise time in [Nano-seconds]
|
spice["rise_time"] = 0.05 # rise time in [Nano-seconds]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue