Merge branch 'dev' into pdriver

This commit is contained in:
Jennifer Eve Sowash 2018-12-06 14:38:08 -08:00
commit 8ea85e3e65
81 changed files with 1646 additions and 1891 deletions

View File

@ -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

View File

@ -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 {} \;

View File

@ -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,6 +794,9 @@ 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):
""" """

View File

@ -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

View File

@ -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,19 +323,20 @@ 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):
""" """

View File

@ -30,6 +30,7 @@ class functional(simulation):
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
self.stored_words = {} self.stored_words = {}
@ -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])

View File

@ -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):

View File

@ -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)

View File

@ -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).")

View File

@ -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,15 +697,18 @@ 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:
@ -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,78 +1095,40 @@ 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)] 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_names, sel_names)) route_map = list(zip(decode_pins, column_mux_pins))
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names } self.create_vertical_channel_route(route_map, offset)
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)
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
@ -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])

View File

@ -132,41 +132,15 @@ 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
wl_wire = self.gen_wl_wire() wl_wire = self.gen_wl_wire()

View File

@ -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,29 +64,38 @@ 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
c = reload(__import__(OPTS.replica_bitline)) c = reload(__import__(OPTS.replica_bitline))
@ -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":
self.place_rbl_in_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.place_rbl_in_row(row=row) self.place_pen_row(row)
self.place_sen_row(row=row+1) row += 1
self.place_rbl(row=row+2) 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): def place_clk_buf_row(self,row):
""" Place the multistage clock buffer below the control flops """ """ Place the multistage clock buffer below the control flops """
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)
clkbuf_offset = vector(x_off,y_off)
self.clkbuf_inst.place(clkbuf_offset) offset = vector(x_off,y_off)
self.clkbuf_inst.place(offset, mirror)
self.row_end_inst.append(self.clkbuf_inst) self.row_end_inst.append(self.clkbuf_inst)
def route_clk_buf(self):
def create_rbl_in_row(self): clk_pin = self.clkbuf_inst.get_pin("A")
self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar", clk_pos = clk_pin.center()
mod=self.nand2) self.add_layout_pin_segment_center(text="clk",
self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"]) layer="metal2",
start=clk_pos,
# input: rbl_in_bar, output: rbl_in end=clk_pos.scale(1,0))
self.rbl_in_inst=self.add_inst(name="inv_rbl_in", self.add_via_center(layers=("metal1","via1","metal2"),
mod=self.inv1) offset=clk_pos)
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
def place_rbl_in_row(self,row): clkbuf_map = zip(["Z"], ["clk_buf"])
x_off = self.ctrl_dff_array.width + self.internal_bus_width 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) (y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.clk_bar_inst.place(offset, mirror)
self.rbl_in_bar_offset = vector(x_off, y_off) x_off += self.inv.width
self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset,
mirror=mirror) offset = vector(x_off,y_off)
x_off += self.nand2.width 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):
# 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):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.rbl_in_inst.place(offset, mirror)
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_dffs(self): def route_sen(self):
""" Route the input inverters """
if self.port_type == "r": out_pos = self.rbl_inst.get_pin("out").bc()
control_inputs = ["cs"] 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: else:
control_inputs = ["cs", "we"] # No we for write-only reports, so use cs
dff_out_map = zip(["dout_bar_{}".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs) input_name = "cs"
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
# 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):
if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r":
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# 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,206 +584,17 @@ 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. """

View File

@ -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"

View File

@ -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)

View File

@ -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),

View File

@ -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)

View File

@ -17,15 +17,11 @@ class hierarchical_decoder(design.design):
""" """
Dynamically generated hierarchical decoder. Dynamically generated hierarchical decoder.
""" """
unique_id = 1
def __init__(self, rows): def __init__(self, rows, height=None):
design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows)) design.design.__init__(self, "hierarchical_decoder_{0}rows_{1}".format(rows,hierarchical_decoder.unique_id))
hierarchical_decoder.unique_id += 1
from importlib import reload
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"))

View File

@ -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
self.number_of_inputs = input_number
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))
from importlib import reload def __init__(self, input_number, height=None):
c = reload(__import__(OPTS.bitcell)) self.number_of_inputs = input_number
self.mod_bitcell = getattr(c, OPTS.bitcell) self.cell_height = height
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
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
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),

View File

@ -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:

View File

@ -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:

View File

@ -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):

View File

@ -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())

View File

@ -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()

View File

@ -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)

View File

@ -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)

128
compiler/pgates/pand2.py Normal file
View File

@ -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

View File

@ -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()
if not OPTS.netlist_only:
self.create_layout()
# Shield the cap, but have at least a stage effort of 4 def create_netlist(self):
input_size = max(1,int(driver_size/stage_effort)) self.add_pins()
self.inv1 = pinv(size=input_size, height=height) # 1 self.create_modules()
self.add_mod(self.inv1) self.create_insts()
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()
#self.offset_all_coordinates()
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,20 +51,32 @@ 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):
# inv1 Z to inv2 A # inv1 Z to inv2 A
@ -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())

View File

@ -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)

View File

@ -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))

View File

@ -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))

View File

@ -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):
@ -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()

View File

@ -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,16 +236,17 @@ 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() pin_names = self.pin_groups.keys()
for pin_name1 in pin_names: for i,pin_name1 in enumerate(pin_names):
for pin_name2 in pin_names: for j,pin_name2 in enumerate(pin_names):
if pin_name1==pin_name2: if i==j:
continue continue
if i>j:
return
self.separate_adjacent_pin(pin_name1, pin_name2, separation) 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):
@ -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)) # If the adjacent grids are a subset of the secondary grids (i.e. not necessary)
bigger.grids.difference_update(bigger_grids) # remove them from each
self.blocked_grids.update(bigger_grids) if adj in bigger.secondary_grids:
return debug.info(3,"Removing {} from bigger secondary {}".format(adj, bigger))
elif smaller_grids.issubset(smaller.secondary_grids): bigger.grids.remove(adj)
debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller)) bigger.secondary_grids.remove(adj)
smaller.grids.difference_update(smaller_grids) self.blocked_grids.add(adj)
self.blocked_grids.update(smaller_grids) elif adj in smaller.secondary_grids:
return 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)
# If that fails, just randomly remove from the bigger one and give a warning.
# This might fail later.
debug.warning("Removing arbitrary grids from a pin group {} {}".format(bigger, bigger_grids))
debug.check(len(bigger.grids)>len(bigger_grids),"Zero size pin group after adjacency removal {} {}".format(bigger, bigger_grids))
bigger.grids.difference_update(bigger_grids)
self.blocked_grids.update(bigger_grids)
@ -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,7 +486,7 @@ 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()
@ -523,28 +494,16 @@ class router(router_tech):
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.
track_pin = self.convert_track_to_shape_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)
# This is the rectangle if we put a pin in the center of the track
track_pin = self.convert_track_to_pin(coord)
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))
inflated_overlap_length = inflated_pin.overlap_length(track_pin)
debug.info(2,"Check overlap: {0} {1} . {2} = {3}".format(coord, inflated_pin.rect, track_pin, inflated_overlap_length))
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length)) # If it overlaps with the pin, it is sufficient
# If it overlaps by more than the min width DRC, we can just use the track if overlap_length==math.inf or overlap_length > 0:
if overlap_length==math.inf or snap_val_to_grid(overlap_length) >= snap_val_to_grid(width): debug.info(2," Overlap: {0} >? {1}".format(overlap_length,0))
debug.info(3," Overlap: {0} >? {1}".format(overlap_length,spacing)) return (coord,None)
return (coord, None) # If it overlaps with the inflated pin, it is partial
# Otherwise, keep track of the partial overlap grids in case we need to patch it later. 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,45 +863,11 @@ 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):
""" """
Remove intermediate points in a rectilinear path or a wave. Remove intermediate points in a rectilinear path or a wave.
@ -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

View File

@ -3,47 +3,61 @@ 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.horiz_layer_name, self.via_layer_name, self.vert_layer_name) = self.layers self.rail_track_width = rail_track_width
(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 # For supplies, we will make the wire wider than the vias
self.vert_track_width = self.max_via_size + self.vert_layer_spacing 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) # 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] 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:
@ -77,3 +91,23 @@ 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)

View File

@ -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,22 +77,14 @@ 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)
@ -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 # Else, if we overlap some of the space track, we can patch it with an enclosure
common_set = supply_tracks & pg.grids #pg.create_simple_overlap_enclosure(pg.grids)
if len(common_set)>0: #pg.add_enclosure(self.cell)
pg.create_simple_overlap_enclosure(common_set)
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)

View File

@ -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

View File

@ -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)

View File

@ -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
@ -63,13 +59,16 @@ class sram_1bank(sram_base):
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 # 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) # 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")
bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf{}".format(port)) control_clk_buf_pos = control_clk_buf_pin.center()
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)]
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
route_map = list(zip(bank_names, dff_names)) bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_names } bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
bank_pins = {key: self.bank_inst.get_pin(key) for key in bank_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin. route_map = list(zip(bank_pins, dff_pins))
all_pins = {**dff_pins, **bank_pins} self.create_horizontal_channel_route(route_map, offset)
self.create_horizontal_channel_route(route_map, all_pins, offset)

View File

@ -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])

View File

@ -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
@ -64,35 +65,50 @@ 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)
@ -404,15 +426,20 @@ class sram_base(design):
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

View File

@ -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):
""" """

View File

@ -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:]

0
compiler/tests/04_pbuf_test.py Normal file → Executable file
View File

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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,

View File

@ -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"

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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))

View File

@ -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")

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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]