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/*
# omit everything in /usr
/usr/*
# ignore the unit tests themselves
*/tests/*
# ignore the debug utilities
debug.py
[paths]
source =
/home/gitlab-runner/builds/2fd64746/0
@ -12,3 +16,13 @@ source =
/home/gitlab-runner/builds/2fd64746/3
/home/gitlab-runner/builds/2fd64746/4
/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)
TEST_DIR = ${CUR_DIR}/tests
MAKEFLAGS += -j 2
MAKEFLAGS += -j 1
# Library test
LIBRARY_TESTS = \
01_library_drc_test.py \
02_library_lvs_test.py
LIBRARY_TESTS = $(shell find ${TEST_DIR} -name 0[1-2]*_test.py)
# Technology and DRC tests (along with ptx)
TECH_TESTS = \
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
TECH_TESTS = $(shell find ${TEST_DIR} -name 03*_test.py)
# Parameterized cells
PCELLS_TESTS = \
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
CELL_TESTS = $(shell find ${TEST_DIR} -name 04*_test.py)
# Dynamically generated modules and arrays
MODULE_TESTS = \
05_bitcell_array_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
MODULE_TESTS = $(shell find ${TEST_DIR} -name 0[5-9]*_test.py)\
$(shell find ${TEST_DIR} -name 1*_test.py)
# Top-level SRAM configurations
TOP_TESTS = \
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
TOP_TESTS = $(shell find ${TEST_DIR} -name 20*_test.py)
# All simulation tests.
CHAR_TESTS = \
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
CHAR_TESTS = $(shell find ${TEST_DIR} -name 2[1-2]*_test.py)
# Keep the model lib test here since it is fast
# and doesn't need simulation.
USAGE_TESTS = \
23_lib_sram_model_test.py \
24_lef_sram_test.py \
25_verilog_sram_test.py
USAGE_TESTS = $(shell find ${TEST_DIR} -name 2[3-9]*_test.py)\
$(shell find ${TEST_DIR} -name 30*_test.py)
ALL_FILES = \
ALL_TESTS = \
${LIBRARY_TESTS} \
${TECH_TESTS} \
${PCELLS_TESTS} \
${MODULES_TESTS} \
${CELL_TESTS} \
${MODULE_TESTS} \
${TOP_TESTS} \
${CHAR_TESTS} \
${USAGE_TESTS}
default all:
.PHONY: ${ALL_TESTS}
$(ALL_FILES):
python ${TEST_DIR}/$@ -t freepdk45
python ${TEST_DIR}/$@ -t scn3me_subm
all: ${ALL_TESTS}
# Library tests
lib: ${LIBRARY_TESTS}
@ -96,10 +48,10 @@ lib: ${LIBRARY_TESTS}
tech: ${TECH_TESTS}
# Dynamically generated cells
pcells: ${PCELLS_TESTS}
cell: ${CELL_TESTS}
# Dynamically generated modules
modules: ${MODULES_TESTS}
module: ${MODULE_TESTS}
# Top level SRAM tests
top: ${TOP_TESTS}
@ -110,6 +62,9 @@ char: ${CHAR_TESTS}
# Usage and file generation
usage: ${USAGE_TESTS}
$(ALL_TESTS):
python3 $@ -t ${TECH}
clean:
find . -name \*.pyc -exec rm {} \;
find . -name \*~ -exec rm {} \;

View File

@ -673,11 +673,13 @@ class layout(lef.lef):
offset=bus_pos,
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"),
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:
pitch = self.m1_pitch
@ -704,15 +706,18 @@ class layout(lef.lef):
# Route each pin to the trunk
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)
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"),
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:
pitch = self.m2_pitch
@ -740,18 +745,21 @@ class layout(lef.lef):
# Route each pin to the trunk
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)
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,
layer_stack=("metal1", "via1", "metal2"), pitch=None,
def create_channel_route(self, netlist,
offset,
layer_stack=("metal1", "via1", "metal2"),
pitch=None,
vertical=False):
"""
The net list is a list of the nets. Each net is a list of pin
names to be connected. Pins is a dictionary of the pin names
to the pin structures. Offset is the lower-left of where the
The net list is a list of the nets. Each net is a list of pins
to be connected. Offset is the lower-left of where the
routing channel will start. This does NOT try to minimize the
number of tracks -- instead, it picks an order to avoid the
vertical conflicts between pins.
@ -786,7 +794,10 @@ class layout(lef.lef):
def vcg_pin_overlap(pin1, pin2, vertical):
""" Check for vertical or horizontal overlap of the two pins """
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin, for example, so the
# extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x-pin2.center().x)<pitch
@ -815,10 +826,7 @@ class layout(lef.lef):
for pin_list in netlist:
net_name = "n{}".format(index)
index += 1
nets[net_name] = []
for pin_name in pin_list:
pin = pins[pin_name]
nets[net_name].append(pin)
nets[net_name] = pin_list
# Find the vertical pin conflicts
# 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):
vcg[net_name2].append(net_name1)
#FIXME: What if we have a cycle?
# list of routes to do
while vcg:
#from pprint import pformat
@ -868,23 +874,21 @@ class layout(lef.lef):
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"),
pitch=None):
"""
Wrapper to create a vertical channel route
"""
self.create_channel_route(netlist, pins, offset, layer_stack,
pitch, vertical=True)
self.create_channel_route(netlist, offset, layer_stack, 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"),
pitch=None):
"""
Wrapper to create a horizontal channel route
"""
self.create_channel_route(netlist, pins, offset,
layer_stack, pitch, vertical=False)
self.create_channel_route(netlist, offset, layer_stack, pitch, vertical=False)
def add_enclosure(self, insts, layer="nwell"):
""" 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,
rotate=float(rotate))
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
if start_layer=="metal1":
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=loc,
rotate=float(rotate))
self.add_layout_pin_rect_center(text=name,
layer="metal3",
offset=loc,
width=via.width,
height=via.height)
if start_layer=="metal1" or start_layer=="metal2":
via=self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=loc,
rotate=float(rotate))
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):
"""

View File

@ -441,10 +441,10 @@ class pin_layout:
"""
Given three co-linear points, determine if q lies on segment pr
"""
if q[0] <= max(p[0], r[0]) and \
q[0] >= min(p[0], r[0]) and \
q[1] <= max(p[1], r[1]) and \
q[1] >= min(p[1], r[1]):
if q.x <= max(p.x, r.x) and \
q.x >= min(p.x, r.x) and \
q.y <= max(p.y, r.y) and \
q.y >= min(p.y, r.y):
return True
return False
@ -473,8 +473,8 @@ class pin_layout:
x = (b2*c1 - b1*c2)/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):
return [x, y]
return r
return None

View File

@ -80,7 +80,7 @@ class pbitcell(design.design):
self.offset_all_coordinates()
gnd_overlap = vector(0, 0.5*contact.well.width)
self.translate_all(gnd_overlap)
self.DRC_LVS()
def add_pins(self):
""" add pins and set names for bitlines and wordlines """
@ -323,20 +323,21 @@ class pbitcell(design.design):
# Add rails for vdd and gnd
gnd_ypos = self.rowline_offset - self.total_ports*self.rowline_spacing
self.gnd_position = vector(0, gnd_ypos)
self.gnd = self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=self.gnd_position,
width=self.width,
height=self.m1_width)
self.add_rect_center(layer="metal1",
offset=self.gnd_position,
width=self.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
self.vdd_position = vector(0, vdd_ypos)
self.vdd = self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=self.vdd_position,
width=self.width,
height=self.m1_width)
self.add_rect_center(layer="metal1",
offset=self.vdd_position,
width=self.width,
height=self.m1_width)
self.add_power_pin("vdd", vector(0,vdd_ypos))
def create_readwrite_ports(self):
"""
Creates read/write ports to the bit cell. A differential pair of transistor can both read and write, like in a 6T cell.

View File

@ -29,6 +29,7 @@ class functional(simulation):
self.set_spice_constants()
self.set_stimulus_variables()
self.create_signal_names()
# Number of checks can be changed
self.num_cycles = 2
@ -141,17 +142,17 @@ class functional(simulation):
sp_read_value = ""
for bit in range(self.word_size):
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
elif value < 0.12 * self.vdd_voltage:
elif value < self.v_low:
sp_read_value = "0" + sp_read_value
else:
error ="FAILED: {0}_{1} value {2} at time {3}n does not fall within noise margins <{4} or >{5}.".format(dout_port,
bit,
value,
eo_period,
0.12*self.vdd_voltage,
0.88*self.vdd_voltage)
self.v_low,
self.v_high)
return (0, error)
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.slew = tech.spice["rise_time"]*2
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
def set_stimulus_variables(self):

View File

@ -3,8 +3,8 @@ num_words = 16
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [ 3.3 ]
temperatures = [ 25 ]
supply_voltages = [5.0]
temperatures = [25]
output_path = "temp"
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...
if OPTS.tech_name == "":
OPTS.tech_name = "scmos"
# Alias SCMOS to AMI 0.5um
# Alias SCMOS to 180nm
if OPTS.tech_name == "scmos":
OPTS.tech_name = "scn4m_subm"
@ -89,6 +89,7 @@ def print_banner():
print("|=========" + dev_info.center(60) + "=========|")
temp_info = "Temp dir: {}".format(OPTS.openram_temp)
print("|=========" + temp_info.center(60) + "=========|")
print("|=========" + "See LICENSE for license info".center(60) + "=========|")
print("|==============================================================================|")
@ -412,6 +413,9 @@ def report_status():
print("Word size: {0}\nWords: {1}\nBanks: {2}".format(OPTS.word_size,
OPTS.num_words,
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:
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 pnor2 import pnor2
from vector import vector
from pinvbuf import pinvbuf
from globals import OPTS
@ -85,11 +84,12 @@ class bank(design.design):
self.add_pin("bank_sel{}".format(port),"INPUT")
for port in self.read_ports:
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:
self.add_pin("w_en{0}".format(port), "INPUT")
for port in self.all_ports:
self.add_pin("clk_buf_bar{0}".format(port),"INPUT")
self.add_pin("clk_buf{0}".format(port),"INPUT")
self.add_pin("wl_en{0}".format(port), "INPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
@ -226,16 +226,19 @@ class bank(design.design):
# UPPER LEFT QUADRANT
# To the left of the bitcell array
# 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)
x_offset += self.row_decoder.width + self.m2_gap
self.row_decoder_offsets[port] = vector(-x_offset,0)
# 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)
# Below the bitcell array with well spacing
x_offset = self.central_bus_width[port] + self.wordline_driver.width
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:
y_offset = 0
y_offset += 2*drc("well_to_well")
@ -283,16 +286,18 @@ class bank(design.design):
# LOWER RIGHT QUADRANT
# To the left of the bitcell array
# 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)
x_offset += self.row_decoder.width + self.m2_gap
self.row_decoder_offsets[port] = vector(x_offset,0)
# 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
x_offset = self.bitcell_array.width + self.central_bus_width[port] + self.wordline_driver.width
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:
y_offset = self.bitcell_array.height
y_offset += 2*drc("well_to_well")
@ -349,21 +354,25 @@ class bank(design.design):
# FIXME: This spacing should be width dependent...
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:
self.input_control_signals = []
port_num = 0
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
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
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
# 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.
self.control_signals = []
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]])
else:
self.control_signals.append(self.input_control_signals[port])
# The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size
else:
self.num_col_addr_lines = 0
# 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
self.col_addr_bus_width = self.m2_pitch*self.num_col_addr_lines
# A space for wells or jogging m2
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)
self.bitcell = self.mod_bitcell()
self.bitcell_array = self.mod_bitcell_array(cols=self.num_cols,
rows=self.num_rows)
self.add_mod(self.bitcell_array)
# 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.br_names = self.bitcell.list_all_br_names()
self.wl_names = self.bitcell.list_all_wl_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):
temp.append(self.bl_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)
@ -664,7 +670,7 @@ class bank(design.design):
temp.append("dec_out{0}_{1}".format(port,row))
for row in range(self.num_rows):
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("gnd")
self.connect_inst(temp)
@ -691,16 +697,19 @@ class bank(design.design):
if self.col_addr_size == 0:
return
elif self.col_addr_size == 1:
from pinvbuf import pinvbuf
self.column_decoder = pinvbuf(height=self.mod_dff.height)
self.add_mod(self.column_decoder)
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:
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:
# No error checking before?
debug.error("Invalid column decoder?",-1)
self.add_mod(self.column_decoder)
self.column_decoder_inst = [None]*len(self.all_ports)
for port in self.all_ports:
self.column_decoder_inst[port] = self.add_inst(name="col_address_decoder{}".format(port),
@ -774,14 +783,14 @@ class bank(design.design):
""" Route the bank select logic. """
if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
bank_sel_signals = ["clk_buf", "w_en", "s_en", "p_en_bar", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en", "gated_s_en", "gated_p_en_bar"]
elif self.port_id[port] == "w":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en"]
bank_sel_signals = ["clk_buf", "w_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_w_en"]
else:
bank_sel_signals = ["clk_buf", "clk_buf_bar", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_s_en"]
bank_sel_signals = ["clk_buf", "s_en", "p_en_bar", "bank_sel"]
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)]
for signal in range(len(copy_control_signals)):
@ -835,8 +844,9 @@ class bank(design.design):
# Port 0
# 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
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
control_bus_length = self.max_y_offset - self.min_y_offset
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_width, 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",
pitch=self.m2_pitch,
offset=control_bus_offset,
@ -847,7 +857,11 @@ class bank(design.design):
# Port 1
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",
pitch=self.m2_pitch,
offset=control_bus_offset,
@ -891,6 +905,9 @@ class bank(design.design):
inst1 = self.bitcell_array_inst
inst1_bl_name = self.bl_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,
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_br_name = "br_{}"
self.connect_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size,
inst1_bl_name=inst1_bl_name, inst1_br_name=inst1_br_name)
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)
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 """
@ -937,7 +954,11 @@ class bank(design.design):
inst1 = self.write_driver_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):
@ -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!
offset = bottom_inst.ul() + vector(0,self.m1_pitch)
for bit in range(num_bits):
bottom_names = [bottom_bl_name.format(bit), bottom_br_name.format(bit)]
top_names = [top_bl_name.format(bit), top_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_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))
bottom_pins = {key: bottom_inst.get_pin(key) for key in bottom_names }
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)
self.create_horizontal_channel_route(route_map, offset)
def connect_bitlines(self, inst1, inst2, num_bits,
@ -1078,79 +1095,41 @@ class bank(design.design):
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
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:
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:
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):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
route_map = list(zip(decode_names, sel_names))
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**decode_pins, **column_mux_pins}
self.create_vertical_channel_route(route_map, all_pins, offset)
def route_column_address_lines_right(self, port):
""" Connecting the select lines of column mux to the address bus """
if not self.col_addr_size>0:
return
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
decode_names = ["Zb", "Z"]
# The Address LSB
self.copy_layout_pin(self.column_decoder_inst[port], "A", "addr{}_0".format(port))
elif self.col_addr_size > 1:
decode_names = []
for i in range(self.num_col_addr_lines):
decode_names.append("out_{}".format(i))
for i in range(self.col_addr_size):
decoder_name = "in_{}".format(i)
addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines*self.m2_pitch, 0)
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
route_map = list(zip(decode_names, sel_names))
decode_pins = {key: self.column_decoder_inst[port].get_pin(key) for key in decode_names }
column_mux_pins = {key: self.column_mux_array_inst[port].get_pin(key) for key in sel_names }
# Combine the dff and bank pins into a single dictionary of pin name to pin.
all_pins = {**decode_pins, **column_mux_pins}
self.create_vertical_channel_route(route_map, all_pins, offset)
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
column_mux_pins = [self.column_mux_array_inst[port].get_pin(x) for x in sel_names]
route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map, offset)
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
@ -1209,7 +1188,7 @@ class bank(design.design):
connection = []
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:
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)
# clk to wordline_driver
control_signal = self.prefix+"clk_buf{}".format(port)
pin_pos = self.wordline_driver_inst[port].get_pin("en").bc()
mid_pos = pin_pos - vector(0,self.m1_pitch)
control_signal = self.prefix+"wl_en{}".format(port)
if port%2:
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_pos = vector(control_x_offset, mid_pos.y)
self.add_wire(("metal1","via1","metal2"),[pin_pos, mid_pos, control_pos])

View File

@ -132,40 +132,14 @@ class bitcell_array(design.design):
# increments to the next row 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 col in range(self.column_size):
inst = self.cell_inst[row,col]
for vdd_pin in inst.get_pins("vdd"):
# Drop to M1 if needed
if vdd_pin.layer == "metal1":
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())
for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name):
self.add_power_pin(pin_name, pin.center(), 0, pin.layer)
def analytical_delay(self, slew, load=0):
from tech import drc

View File

@ -4,11 +4,12 @@ from tech import drc, parameter
import debug
import contact
from pinv import pinv
from pbuf import pbuf
from pand2 import pand2
from pnand2 import pnand2
from pnand3 import pnand3
from pinvbuf import pinvbuf
from dff_inv import dff_inv
from dff_inv_array import dff_inv_array
from dff_buf import dff_buf
from dff_buf_array import dff_buf_array
import math
from vector import vector
from globals import OPTS
@ -47,9 +48,7 @@ class control_logic(design.design):
""" Create layout and route between modules """
self.place_instances()
self.route_all()
#self.add_lvs_correspondence_points()
self.DRC_LVS()
@ -65,28 +64,37 @@ class control_logic(design.design):
def add_modules(self):
""" Add all the required modules """
dff = dff_inv()
dff = dff_buf()
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.nand2 = pnand2(height=dff_height)
self.add_mod(self.nand2)
self.nand3 = pnand3(height=dff_height)
self.add_mod(self.nand3)
self.and2 = pand2(size=4,height=dff_height)
self.add_mod(self.and2)
# Special gates: inverters for buffering
# Size the clock for the number of rows (fanout)
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.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.add_mod(self.inv1)
self.inv2 = pinv(size=4, height=dff_height)
self.add_mod(self.inv2)
self.inv8 = pinv(size=16, height=dff_height)
self.inv8 = pinv(size=8, height=dff_height)
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"):
from importlib import reload
@ -127,20 +135,22 @@ class control_logic(design.design):
# list of output control signals (for making a vertical bus)
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:
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
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch
# Outputs to the bank
if self.port_type == "r":
self.output_list = ["s_en"]
elif self.port_type == "w":
self.output_list = ["w_en"]
if self.port_type == "rw":
self.output_list = ["s_en", "w_en", "p_en_bar"]
elif self.port_type == "r":
self.output_list = ["s_en", "p_en_bar"]
else:
self.output_list = ["s_en", "w_en"]
self.output_list.append("clk_buf_bar")
self.output_list = ["w_en"]
self.output_list.append("wl_en")
self.output_list.append("clk_buf")
self.supply_list = ["vdd", "gnd"]
@ -157,11 +167,16 @@ class control_logic(design.design):
def create_instances(self):
""" Create all the instances """
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"):
self.create_we_row()
if (self.port_type == "rw") or (self.port_type == "r"):
self.create_wen_row()
if self.port_type == "rw":
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_rbl()
@ -175,19 +190,33 @@ class control_logic(design.design):
# Add the control flops on the left of the bus
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
# Add the logic on the right of the bus
self.place_clk_row(row=row) # clk is a double-high cell
row += 2
self.place_clk_buf_row(row)
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"):
self.place_we_row(row=row)
self.place_wen_row(row)
height = self.w_en_inst.uy()
control_center_y = self.w_en_inst.uy()
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_rbl_in_row(row=row)
self.place_sen_row(row=row+1)
self.place_rbl(row=row+2)
if self.port_type == "rw":
self.place_rbl_in_row(row)
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_pen_row(row)
row += 1
self.place_sen_row(row)
row += 1
self.place_rbl(row)
height = self.rbl_inst.uy()
control_center_y = self.rbl_inst.by()
@ -197,122 +226,350 @@ class control_logic(design.design):
# Extra pitch on top and right
self.height = height + 2*self.m1_pitch
# 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"):
self.width = max(self.rbl_inst.rx(), max([inst.rx() for inst in self.row_end_inst])) + self.m2_pitch
else:
self.width = max([inst.rx() for inst in self.row_end_inst]) + self.m2_pitch
self.width = max(self.rbl_inst.rx() , self.width)
self.width += self.m2_pitch
def route_all(self):
""" Routing between modules """
self.route_rails()
self.route_dffs()
self.route_wlen()
if (self.port_type == "rw") or (self.port_type == "w"):
self.route_wen()
if (self.port_type == "rw") or (self.port_type == "r"):
self.route_rbl_in()
self.route_pen()
self.route_sen()
self.route_clk()
self.route_clk_buf()
self.route_gated_clk_bar()
self.route_gated_clk_buf()
self.route_supply()
def create_rbl(self):
""" 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",
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):
""" 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 to the right of the control rows and routing channel
self.replica_bitline_offset = vector(0, y_off)
self.rbl_inst.place(self.replica_bitline_offset)
offset = vector(0, y_off)
self.rbl_inst.place(offset)
def create_clk_row(self):
""" Create the multistage clock buffer """
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
self.clkbuf_inst = self.add_inst(name="clkbuf",
mod=self.clkbuf)
self.connect_inst(["clk","clk_buf_bar","clk_buf","vdd","gnd"])
def place_clk_row(self,row):
""" Place the multistage clock buffer below the control flops """
x_off = self.ctrl_dff_array.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
clkbuf_offset = vector(x_off,y_off)
self.clkbuf_inst.place(clkbuf_offset)
self.row_end_inst.append(self.clkbuf_inst)
self.connect_inst(["clk","clk_buf","vdd","gnd"])
def place_clk_buf_row(self,row):
""" Place the multistage clock buffer below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.clkbuf_inst.place(offset, mirror)
self.row_end_inst.append(self.clkbuf_inst)
def route_clk_buf(self):
clk_pin = self.clkbuf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_segment_center(text="clk",
layer="metal2",
start=clk_pos,
end=clk_pos.scale(1,0))
self.add_via_center(layers=("metal1","via1","metal2"),
offset=clk_pos)
clkbuf_map = zip(["Z"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clkbuf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.clkbuf_inst.get_pin("Z").center())
self.connect_output(self.clkbuf_inst, "Z", "clk_buf")
def create_gated_clk_bar_row(self):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv)
self.connect_inst(["clk_buf","clk_bar","vdd","gnd"])
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2)
self.connect_inst(["cs","clk_bar","gated_clk_bar","vdd","gnd"])
def place_gated_clk_bar_row(self,row):
""" Place the gated clk logic below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.clk_bar_inst.place(offset, mirror)
x_off += self.inv.width
offset = vector(x_off,y_off)
self.gated_clk_bar_inst.place(offset, mirror)
self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
mid1 = vector(in_pos.x,out_pos.y)
self.add_path("metal1",[out_pos, mid1, in_pos])
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["A"], ["cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_bar_inst.get_pin("A").center())
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_bar_inst.get_pin("Z").center())
def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
mod=self.and2)
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"])
def place_gated_clk_buf_row(self,row):
""" Place the gated clk logic below the control flops """
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off,y_off)
self.gated_clk_buf_inst.place(offset, mirror)
self.row_end_inst.append(self.gated_clk_buf_inst)
def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets, ("metal3", "via2", "metal2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=("metal1","via1","metal2"),
offset=self.gated_clk_buf_inst.get_pin("Z").center())
def create_wlen_row(self):
# input pre_p_en, output: wl_en
self.wl_en_inst=self.add_inst(name="buf_wl_en",
mod=self.buf16)
self.connect_inst(["gated_clk_bar", "wl_en", "vdd", "gnd"])
def place_wlen_row(self, row):
x_off = self.control_x_offset
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.wl_en_inst.place(offset, mirror)
self.row_end_inst.append(self.wl_en_inst)
def route_wlen(self):
wlen_map = zip(["A"], ["gated_clk_bar"])
self.connect_vertical_bus(wlen_map, self.wl_en_inst, self.rail_offsets)
self.connect_output(self.wl_en_inst, "Z", "wl_en")
def create_rbl_in_row(self):
self.rbl_in_bar_inst=self.add_inst(name="nand2_rbl_in_bar",
mod=self.nand2)
self.connect_inst(["clk_buf_bar", "cs", "rbl_in_bar", "vdd", "gnd"])
# input: rbl_in_bar, output: rbl_in
self.rbl_in_inst=self.add_inst(name="inv_rbl_in",
mod=self.inv1)
self.connect_inst(["rbl_in_bar", "rbl_in", "vdd", "gnd"])
# input: gated_clk_bar, we_bar, output: rbl_in
self.rbl_in_inst=self.add_inst(name="and2_rbl_in",
mod=self.and2)
self.connect_inst(["gated_clk_bar", "we_bar", "rbl_in", "vdd", "gnd"])
def place_rbl_in_row(self,row):
x_off = self.ctrl_dff_array.width + self.internal_bus_width
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_bar_offset = vector(x_off, y_off)
self.rbl_in_bar_inst.place(offset=self.rbl_in_bar_offset,
mirror=mirror)
x_off += self.nand2.width
self.rbl_in_offset = vector(x_off, y_off)
self.rbl_in_inst.place(offset=self.rbl_in_offset,
mirror=mirror)
self.row_end_inst.append(self.rbl_in_inst)
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):
""" Create the sense enable buffer. """
# input: pre_s_en, output: pre_s_en_bar
self.pre_s_en_bar_inst=self.add_inst(name="inv_pre_s_en_bar",
mod=self.inv2)
self.connect_inst(["pre_s_en", "pre_s_en_bar", "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"])
# BUFFER FOR S_EN
# input: pre_s_en, output: s_en
self.s_en_inst=self.add_inst(name="buf_s_en",
mod=self.buf8)
self.connect_inst(["pre_s_en", "s_en", "vdd", "gnd"])
def place_sen_row(self,row):
"""
The sense enable buffer gets placed to the far right of the
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)
self.pre_s_en_bar_offset = vector(x_off, y_off)
self.pre_s_en_bar_inst.place(offset=self.pre_s_en_bar_offset,
mirror=mirror)
x_off += self.inv2.width
offset = vector(x_off, y_off)
self.s_en_inst.place(offset, mirror)
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)
def route_sen(self):
out_pos = self.rbl_inst.get_pin("out").bc()
in_pos = self.s_en_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y)
self.add_wire(("metal1","via1","metal2"),[out_pos, mid1,in_pos])
self.connect_output(self.s_en_inst, "Z", "s_en")
def create_wen_row(self):
# input: we (or cs) output: w_en
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
# BUFFER FOR W_EN
self.w_en_inst = self.add_inst(name="buf_w_en_buf",
mod=self.buf8)
self.connect_inst([input_name, "w_en", "vdd", "gnd"])
def place_wen_row(self,row):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
offset = vector(x_off, y_off)
self.w_en_inst.place(offset, mirror)
self.row_end_inst.append(self.w_en_inst)
def route_wen(self):
if self.port_type == "rw":
input_name = "we"
else:
# No we for write-only reports, so use cs
input_name = "cs"
wen_map = zip(["A"], [input_name])
self.connect_vertical_bus(wen_map, self.w_en_inst, self.rail_offsets)
self.connect_output(self.w_en_inst, "Z", "w_en")
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
self.connect_inst(self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0,0))
def route_dffs(self):
""" Route the input inverters """
if self.port_type == "r":
control_inputs = ["cs"]
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:
control_inputs = ["cs", "we"]
dff_out_map = zip(["dout_bar_{}".format(i) for i in range(2*self.num_control_signals - 1)], control_inputs)
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets)
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
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
@ -327,207 +584,18 @@ class control_logic(design.design):
if (self.port_type == "rw"):
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):
""" Compute the y-offset and mirroring """
y_off = row*self.inv1.height
y_off = row*self.and2.height
if row % 2:
y_off += self.inv1.height
y_off += self.and2.height
mirror="MX"
else:
mirror="R0"
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):
""" 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={}
for row in range(self.rows):
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,
mod=self.dff)
self.connect_inst([self.get_din_name(row,col),
@ -71,7 +71,7 @@ class dff_array(design.design):
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)
name = "dff_r{0}_c{1}".format(row,col)
if (row % 2 == 0):
base = vector(col*self.dff.width,row*self.dff.height)
mirror = "R0"

View File

@ -102,8 +102,7 @@ class dff_buf(design.design):
mid_x_offset = 0.5*(a1_pin.cx() + q_pin.cx())
mid1 = vector(mid_x_offset, q_pin.cy())
mid2 = vector(mid_x_offset, a1_pin.cy())
self.add_path("metal3",
[q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_path("metal3", [q_pin.center(), mid1, mid2, a1_pin.center()])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=q_pin.center())
self.add_via_center(layers=("metal2","via2","metal3"),
@ -114,8 +113,10 @@ class dff_buf(design.design):
# Route inv1 z to inv2 a
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
mid_x_offset = 0.5*(z1_pin.cx() + a2_pin.cx())
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):
@ -150,18 +151,22 @@ class dff_buf(design.design):
height=din_pin.height())
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",
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"),
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",
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"),
offset=dout_pin.center())
offset=qb_pos)

View File

@ -61,7 +61,7 @@ class dff_buf_array(design.design):
self.dff_insts={}
for row in range(self.rows):
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,
mod=self.dff)
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.
"""
def __init__(self, rows):
design.design.__init__(self, "hierarchical_decoder_{0}rows".format(rows))
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
unique_id = 1
def __init__(self, rows, height=None):
design.design.__init__(self, "hierarchical_decoder_{0}rows_{1}".format(rows,hierarchical_decoder.unique_id))
hierarchical_decoder.unique_id += 1
self.NAND_FORMAT = "DEC_NAND_{0}"
self.INV_FORMAT = "DEC_INV_{0}"
@ -33,6 +29,7 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = []
self.pre3x8_inst = []
self.cell_height = height
self.rows = rows
self.num_inputs = int(math.log(self.rows, 2))
(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()
def add_modules(self):
self.inv = pinv()
self.inv = pinv(height=self.cell_height)
self.add_mod(self.inv)
self.nand2 = pnand2()
self.nand2 = pnand2(height=self.cell_height)
self.add_mod(self.nand2)
self.nand3 = pnand3()
self.nand3 = pnand3(height=self.cell_height)
self.add_mod(self.nand3)
self.add_decoders()
def add_decoders(self):
""" 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.pre3_8 = pre3x8()
self.pre3_8 = pre3x8(height=self.cell_height)
self.add_mod(self.pre3_8)
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):
for i in range(len(self.predec_groups[0])):
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)
self.nand_inst.append(self.add_inst(name=name,
mod=self.nand2))
@ -352,8 +349,8 @@ class hierarchical_decoder(design.design):
for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])):
row = len(self.predec_groups[1])*len(self.predec_groups[2]) * i \
+ len(self.predec_groups[2])*j + k
row = (len(self.predec_groups[0])*len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0])*j + i
name = self.NAND_FORMAT.format(row)
self.nand_inst.append(self.add_inst(name=name,
@ -523,8 +520,8 @@ class hierarchical_decoder(design.design):
"""
row_index = 0
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?
predecode_name = "predecode_{}".format(index_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
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_C in self.predec_groups[2]:
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
predecode_name = "predecode_{}".format(index_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 pnand3 import pnand3
class hierarchical_predecode(design.design):
"""
Pre 2x4 and 3x8 decoder shared code.
"""
def __init__(self, input_number):
unique_id = 1
def __init__(self, input_number, height=None):
self.number_of_inputs = input_number
self.cell_height = height
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
c = reload(__import__(OPTS.bitcell))
self.mod_bitcell = getattr(c, OPTS.bitcell)
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):
for k in range(self.number_of_inputs):
@ -34,7 +33,7 @@ class hierarchical_predecode(design.design):
def add_modules(self):
""" Add the INV and NAND gate modules """
self.inv = pinv()
self.inv = pinv(height=self.cell_height)
self.add_mod(self.inv)
self.add_nand(self.number_of_inputs)
@ -43,9 +42,9 @@ class hierarchical_predecode(design.design):
def add_nand(self,inputs):
""" Create the NAND for the predecode input stage """
if inputs==2:
self.nand = pnand2()
self.nand = pnand2(height=self.cell_height)
elif inputs==3:
self.nand = pnand3()
self.nand = pnand3(height=self.cell_height)
else:
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. """
self.in_inst = []
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,
mod=self.inv))
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. """
self.inv_inst = []
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,
mod=self.inv))
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.
"""
def __init__(self):
hierarchical_predecode.__init__(self, 2)
def __init__(self, height=None):
hierarchical_predecode.__init__(self, 2, height)
self.create_netlist()
if not OPTS.netlist_only:

View File

@ -9,8 +9,8 @@ class hierarchical_predecode3x8(hierarchical_predecode):
"""
Pre 3x8 decoder used in hierarchical_decoder.
"""
def __init__(self):
hierarchical_predecode.__init__(self, 3)
def __init__(self, height=None):
hierarchical_predecode.__init__(self, 3, height)
self.create_netlist()
if not OPTS.netlist_only:

View File

@ -33,7 +33,7 @@ class precharge_array(design.design):
for i in range(self.columns):
self.add_pin("bl_{0}".format(i))
self.add_pin("br_{0}".format(i))
self.add_pin("en")
self.add_pin("en_bar")
self.add_pin("vdd")
def create_netlist(self):
@ -59,9 +59,9 @@ class precharge_array(design.design):
def add_layout_pins(self):
self.add_layout_pin(text="en",
self.add_layout_pin(text="en_bar",
layer="metal1",
offset=self.pc_cell.get_pin("en").ll(),
offset=self.pc_cell.get_pin("en_bar").ll(),
width=self.width,
height=drc("minwidth_metal1"))
@ -94,7 +94,7 @@ class precharge_array(design.design):
mod=self.pc_cell,
offset=offset)
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):

View File

@ -265,15 +265,8 @@ class replica_bitline(design.design):
pin = self.rbl_inv_inst.get_pin("vdd")
self.add_power_pin("vdd", pin.lc())
# Replica bitcell needs to be routed up to M3
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
# 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])
self.add_power_pin("vdd", pin.center(), 0, pin.layer)
for pin in self.rbc_inst.get_pins("gnd"):
self.add_power_pin("gnd", pin.center())

View File

@ -44,7 +44,7 @@ class wordline_driver(design.design):
# Outputs from wordline_driver.
for i in range(self.rows):
self.add_pin("wl_{0}".format(i))
self.add_pin("en")
self.add_pin("en_bar")
self.add_pin("vdd")
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. """
# 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()
for num in range(self.rows):
# this will result in duplicate polygons for rails, but who cares
@ -95,24 +95,16 @@ class wordline_driver(design.design):
def create_drivers(self):
self.inv1_inst = []
self.nand_inst = []
self.inv2_inst = []
for row in range(self.rows):
name_inv1 = "wl_driver_inv_en{}".format(row)
name_nand = "wl_driver_nand{}".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
self.nand_inst.append(self.add_inst(name=name_nand,
mod=self.nand2))
self.connect_inst(["en_bar_{0}".format(row),
self.connect_inst(["en_bar",
"in_{0}".format(row),
"wl_bar_{0}".format(row),
"vdd", "gnd"])
@ -125,8 +117,7 @@ class wordline_driver(design.design):
def place_drivers(self):
inv1_xoffset = 2*self.m1_width + 5*self.m1_space
nand2_xoffset = inv1_xoffset + self.inv.width
nand2_xoffset = 2*self.m1_width + 5*self.m1_space
inv2_xoffset = nand2_xoffset + self.nand2.width
self.width = inv2_xoffset + self.inv.width
@ -140,13 +131,9 @@ class wordline_driver(design.design):
y_offset = self.inv.height*row
inst_mirror = "R0"
inv1_offset = [inv1_xoffset, y_offset]
nand2_offset=[nand2_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
self.nand_inst[row].place(offset=nand2_offset,
mirror=inst_mirror)
@ -159,7 +146,7 @@ class wordline_driver(design.design):
""" Route all of the signals """
# Wordline enable connection
en_pin=self.add_layout_pin(text="en",
en_pin=self.add_layout_pin(text="en_bar",
layer="metal2",
offset=[self.m1_width + 2*self.m1_space,0],
width=self.m2_width,
@ -167,12 +154,11 @@ class wordline_driver(design.design):
for row in range(self.rows):
inv1_inst = self.inv1_inst[row]
nand_inst = self.nand_inst[row]
inv2_inst = self.inv2_inst[row]
# en connection
a_pin = inv1_inst.get_pin("A")
# en_bar connection
a_pin = nand_inst.get_pin("A")
a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x,a_pos.y)
self.add_segment_center(layer="metal1",
@ -181,13 +167,6 @@ class wordline_driver(design.design):
self.add_via_center(layers=("metal1", "via1", "metal2"),
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
zr_pos = nand_inst.get_pin("Z").rc()
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):
self.driver_insts = {}
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)
self.driver_insts[index]=self.add_inst(name=name,
mod=self.driver)

View File

@ -26,6 +26,10 @@ if len(args) != 1:
# These depend on arguments, so don't load them until now.
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)
# Only print banner here so it's not in unit tests
@ -34,10 +38,14 @@ print_banner()
# Output info about this run
report_status()
# Start importing design modules after we have the config file
import verify
from sram import sram
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 *
output_extensions = ["sp","v","lib"]
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,sep="\n")
# Keep track of running stats
start_time = datetime.datetime.now()
print_time("Start",start_time)
# Configure the SRAM organization
c = sram_config(word_size=OPTS.word_size,
num_words=OPTS.num_words)
# import SRAM test generation
from sram import sram
s = sram(sram_config=c,
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 design
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from pinv import pinv
import pgate
class pbuf(design.design):
class pbuf(pgate.pgate):
"""
This is a simple buffer used for driving loads.
"""
@ -16,39 +16,32 @@ class pbuf(design.design):
unique_id = 1
def __init__(self, driver_size=4, height=bitcell.height, name=""):
def __init__(self, size=4, height=None, name=""):
stage_effort = 4
# FIXME: Change the number of stages to support high drives.
self.stage_effort = 4
self.size = size
self.height = height
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
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
pgate.pgate.__init__(self, name, height)
debug.info(1, "creating {0} with size of {1}".format(self.name,self.size))
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(driver_size/stage_effort))
self.inv1 = pinv(size=input_size, height=height) # 1
self.add_mod(self.inv1)
self.inv2 = pinv(size=driver_size, height=height) # 2
self.add_mod(self.inv2)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
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_netlist(self):
self.add_pins()
self.create_modules()
self.create_insts()
def create_layout(self):
self.add_pins()
self.add_insts()
self.width = self.inv1.width + self.inv2.width
self.place_insts()
self.add_wires()
self.add_layout_pins()
@ -58,19 +51,31 @@ class pbuf(design.design):
self.add_pin("vdd")
self.add_pin("gnd")
def add_insts(self):
# Add INV1 to the right
def create_modules(self):
# 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",
mod=self.inv1,
offset=vector(0,0))
mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
# Add INV2 to the right
self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv2,
offset=vector(self.inv1_inst.rx(),0))
mod=self.inv2)
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):
@ -100,17 +105,17 @@ class pbuf(design.design):
z_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer="metal2",
offset=z_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=z_pin.center())
layer=z_pin.layer,
offset=z_pin.center(),
width=z_pin.width(),
height=z_pin.height())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer="metal2",
offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
offset=a_pin.center())
layer=a_pin.layer,
offset=a_pin.center(),
width=a_pin.width(),
height=a_pin.height())

View File

@ -33,7 +33,6 @@ class pnand2(pgate.pgate):
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
#self.DRC_LVS()
def create_netlist(self):
@ -192,26 +191,33 @@ class pnand2(pgate.pgate):
""" Route the Z output """
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
# 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
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"),
offset=pmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center())
self.add_contact_center(layers=("metal1", "via1", "metal2"),
offset=mid_offset,
offset=out_offset,
rotate=90)
# 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
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
offset=mid_offset,
offset=out_offset,
width=contact.m1m2.first_layer_height,
height=contact.m1m2.first_layer_width)

View File

@ -51,7 +51,7 @@ class precharge(pgate.pgate):
self.DRC_LVS()
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):
"""
@ -92,15 +92,15 @@ class precharge(pgate.pgate):
self.lower_pmos_inst=self.add_inst(name="lower_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",
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",
mod=self.pmos)
self.connect_inst(["br", "en", "vdd", "vdd"])
self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self):
@ -161,7 +161,7 @@ class precharge(pgate.pgate):
rotate=90)
# 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",
start=offset.scale(0,1),
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.
area_sd = 2.5*drc("minwidth_poly")*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.mults,
self.tx_width,
drc("minwidth_poly"),
perimeter_sd,
area_sd)
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.tx_width,
drc("minwidth_poly"),
perimeter_sd,
area_sd)
self.spice.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))

View File

@ -172,7 +172,6 @@ class pin_group:
ymax = max(plc.y,elc.y)
ll = vector(plc.x, ymin)
ur = vector(prc.x, ymax)
p = pin_layout(pin.name, [ll, ur], pin.layer)
elif pin.yoverlaps(enclosure):
# Is it horizontal overlap, extend pin shape to enclosure
pbc = pin.bc()
@ -183,7 +182,6 @@ class pin_group:
xmax = max(pbc.x,ebc.x)
ll = vector(xmin, pbc.y)
ur = vector(xmax, puc.y)
p = pin_layout(pin.name, [ll, ur], pin.layer)
else:
# Neither, so we must do a corner-to corner
pc = pin.center()
@ -194,8 +192,10 @@ class pin_group:
ymax = max(pc.y, ec.y)
ll = vector(xmin, ymin)
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
def find_above_connector(self, pin, enclosures):
@ -226,7 +226,7 @@ class pin_group:
# If it already overlaps, no connector needed
if above_item.overlaps(pin):
return None
# Otherwise, make a connector to the item
p = self.compute_connector(pin, above_item)
return p
@ -485,7 +485,9 @@ class pin_group:
for pin_list in self.pins:
if not self.overlap_any_shape(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)
@ -559,15 +561,13 @@ class pin_group:
of any grid in the other set.
"""
# We could optimize this to just check the boundaries
g1_grids = set()
g2_grids = set()
adj_grids = set()
for g1 in self.grids:
for g2 in other.grids:
if g1.distance(g2) <= separation:
g1_grids.add(g1)
g2_grids.add(g2)
adj_grids.add(g1)
return g1_grids,g2_grids
return adj_grids
def convert_pin(self):
"""
@ -576,14 +576,16 @@ class pin_group:
should be either blocked or part of the pin.
"""
pin_set = set()
partial_set = set()
blockage_set = set()
for pin_list in self.pins:
for pin in pin_list:
debug.info(2," Converting {0}".format(pin))
# Determine which tracks the pin overlaps
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(pin_in_tracks)
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(sufficient)
partial_set.update(insufficient)
# Blockages will be a super-set of pins since it uses the inflated pin shape.
blockage_in_tracks = self.router.convert_blockage(pin)
@ -595,89 +597,94 @@ class pin_group:
if len(shared_set)>0:
debug.info(2,"Removing pins {}".format(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
if len(shared_set)>0:
debug.info(2,"Removing blocks {}".format(shared_set))
blockage_set.difference_update(shared_set)
# 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))
for pin_list in self.pins:
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
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
pin_set.update(pin_in_tracks)
(sufficient,insufficient)=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
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))
self.router.write_debug_gds("blocked_pin.gds")
# We need to route each of the components, so don't combine the groups
self.grids = pin_set | blockage_set
# Remember the secondary grids for removing adjacent pins in wide metal spacing
self.secondary_grids = blockage_set - pin_set
# Consider all the grids that would be blocked
self.grids = pin_set | partial_set
# Remember the secondary grids for removing adjacent pins
self.secondary_grids = partial_set
debug.info(2," pins {}".format(self.grids))
debug.info(2," secondary {}".format(self.secondary_grids))
def recurse_simple_overlap_enclosure(self, start_set, direct):
"""
Recursive function to return set of tracks that connects to
the actual supply rail wire in a given direction (or terminating
when any track is no longer in the supply rail.
"""
next_set = grid_utils.expand_border(start_set, direct)
# def recurse_simple_overlap_enclosure(self, start_set, direct):
# """
# Recursive function to return set of tracks that connects to
# the actual supply rail wire in a given direction (or terminating
# when any track is no longer in the supply rail.
# """
# next_set = grid_utils.expand_border(start_set, direct)
supply_tracks = self.router.supply_rail_tracks[self.name]
supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
# supply_tracks = self.router.supply_rail_tracks[self.name]
# supply_wire_tracks = self.router.supply_rail_wire_tracks[self.name]
supply_overlap = next_set & supply_tracks
wire_overlap = next_set & supply_wire_tracks
# supply_overlap = next_set & supply_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 len(wire_overlap)==len(start_set):
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
elif len(supply_overlap)==len(start_set):
recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
new_set = start_set | supply_overlap | recurse_set
else:
# If we got no next set, we are done, can't expand!
new_set = set()
# # If the rail overlap is the same, we are done, since we connected to the actual wire
# if len(wire_overlap)==len(start_set):
# 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
# elif len(supply_overlap)==len(start_set):
# recurse_set = self.recurse_simple_overlap_enclosure(supply_overlap, direct)
# new_set = start_set | supply_overlap | recurse_set
# else:
# # If we got no next set, we are done, can't expand!
# new_set = set()
return new_set
# return new_set
def create_simple_overlap_enclosure(self, start_set):
"""
This takes a set of tracks that overlap a supply rail and creates an enclosure
that is ensured to overlap the supply rail wire.
It then adds rectangle(s) for the enclosure.
"""
additional_set = set()
# Check the layer of any element in the pin to determine which direction to route it
e = next(iter(start_set))
new_set = start_set.copy()
if e.z==0:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
else:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
if not new_set:
new_set = self.recurse_simple_overlap_enclosure(start_set, direction.WEST)
# def create_simple_overlap_enclosure(self, start_set):
# """
# This takes a set of tracks that overlap a supply rail and creates an enclosure
# that is ensured to overlap the supply rail wire.
# It then adds rectangle(s) for the enclosure.
# """
# additional_set = set()
# # Check the layer of any element in the pin to determine which direction to route it
# e = next(iter(start_set))
# new_set = start_set.copy()
# if e.z==0:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.NORTH)
# if not new_set:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.SOUTH)
# else:
# new_set = self.recurse_simple_overlap_enclosure(start_set, direction.EAST)
# if not new_set:
# 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
self.grids.update(new_set)
# # Expand the pin grid set to include some extra grids that connect the supply rail
# self.grids.update(new_set)
# Add the inflated set so we don't get wide metal spacing issues (if it exists)
self.blockages.update(grid_utils.inflate_set(new_set,self.router.supply_rail_space_width))
# # Block the grids
# self.blockages.update(new_set)
# Add the polygon enclosures and set this pin group as routed
self.set_routed()
self.enclosures = self.compute_enclosures()
# # Add the polygon enclosures and set this pin group as routed
# self.set_routed()
# self.enclosures = self.compute_enclosures()

View File

@ -21,12 +21,12 @@ class router(router_tech):
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
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
@ -70,8 +70,7 @@ class router(router_tech):
self.boundary = self.layout.measureBoundary(self.top_name)
# These must be un-indexed to get rid of the matrix type
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.track_widths.scale(5,5)
self.ur = vector(self.boundary[1][0], self.boundary[1][1])
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].
"""
debug.info(1,"Finding pins for {}.".format(pin_name))
#start_time = datetime.now()
self.retrieve_pins(pin_name)
#print_time("Retrieved pins",datetime.now(), start_time)
#start_time = datetime.now()
self.analyze_pins(pin_name)
#print_time("Analyzed pins",datetime.now(), start_time)
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 ignores shapes that were pins
#start_time = datetime.now()
self.find_blockages()
#print_time("Find blockags",datetime.now(), start_time)
# Convert the blockages to grid units
#start_time = datetime.now()
self.convert_blockages()
#print_time("Find blockags",datetime.now(), start_time)
# This will convert the pins to grid units
# 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:
self.convert_pins(pin)
#print_time("Convert pins",datetime.now(), start_time)
#start_time = datetime.now()
for pin in pin_list:
self.combine_adjacent_pins(pin)
#print_time("Combine pins",datetime.now(), start_time)
#self.write_debug_gds("debug_combine_pins.gds",stop_program=True)
#for pin in pin_list:
# self.combine_adjacent_pins(pin)
# 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
#start_time = datetime.now()
self.separate_adjacent_pins(self.supply_rail_space_width)
#print_time("Separate pins",datetime.now(), start_time)
# For debug
#self.separate_adjacent_pins(1)
self.separate_adjacent_pins(0)
# Enclose the continguous grid units in a metal rectangle to fix some DRCs
#start_time = datetime.now()
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):
"""
Find pins that have adjacent routing tracks and merge them into a
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))
# Find all adjacencies
@ -257,18 +236,19 @@ class router(router_tech):
This will try to separate all grid pins by the supplied number of separation
tracks (default is to prevent adjacency).
"""
debug.info(1,"Separating adjacent pins.")
# Commented out to debug with SCMOS
#if separation==0:
# return
pin_names = self.pin_groups.keys()
for pin_name1 in pin_names:
for pin_name2 in pin_names:
if pin_name1==pin_name2:
continue
self.separate_adjacent_pin(pin_name1, pin_name2, separation)
pin_names = self.pin_groups.keys()
for i,pin_name1 in enumerate(pin_names):
for j,pin_name2 in enumerate(pin_names):
if i==j:
continue
if i>j:
return
self.separate_adjacent_pin(pin_name1, pin_name2, separation)
def separate_adjacent_pin(self, pin_name1, pin_name2, separation):
"""
Go through all of the pin groups and check if any other pin group is
@ -277,50 +257,57 @@ class router(router_tech):
Try to do this intelligently to keep th pins enclosed.
"""
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 index2,pg2 in enumerate(self.pin_groups[pin_name2]):
# FIgXME: Use separation distance and edge grids only
grids_g1, grids_g2 = pg1.adjacent_grids(pg2, separation)
adj_grids = pg1.adjacent_grids(pg2, separation)
removed_grids += len(adj_grids)
# These should have the same length, so...
if len(grids_g1)>0:
debug.info(3,"Adjacent grids {0} {1} {2} {3}".format(index1,grids_g1,index2,grids_g2))
self.remove_adjacent_grid(pg1, grids_g1, pg2, grids_g2)
if len(adj_grids)>0:
debug.info(3,"Adjacent grids {0} {1} adj={2}".format(index1,index2,adj_grids))
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.
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():
bigger = pg1
bigger_grids = grids1
smaller = pg2
smaller_grids = grids2
else:
bigger = pg2
bigger_grids = grids2
smaller = pg1
smaller_grids = grids1
# First, see if we can remove grids that are in the secondary grids
# i.e. they aren't necessary to the pin grids
if bigger_grids.issubset(bigger.secondary_grids):
debug.info(3,"Removing {} from bigger {}".format(str(bigger_grids), bigger))
bigger.grids.difference_update(bigger_grids)
self.blocked_grids.update(bigger_grids)
return
elif smaller_grids.issubset(smaller.secondary_grids):
debug.info(3,"Removing {} from smaller {}".format(str(smaller_grids), smaller))
smaller.grids.difference_update(smaller_grids)
self.blocked_grids.update(smaller_grids)
return
for adj in adj_grids:
# 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)
# If the adjacent grids are a subset of the secondary grids (i.e. not necessary)
# remove them from each
if adj in bigger.secondary_grids:
debug.info(3,"Removing {} from bigger secondary {}".format(adj, bigger))
bigger.grids.remove(adj)
bigger.secondary_grids.remove(adj)
self.blocked_grids.add(adj)
elif adj in smaller.secondary_grids:
debug.info(3,"Removing {} from smaller secondary {}".format(adj, smaller))
smaller.grids.remove(adj)
smaller.secondary_grids.remove(adj)
self.blocked_grids.add(adj)
else:
# If we couldn't remove from a secondary grid, we must remove from the primary
# grid of at least one pin
if adj in bigger.grids:
debug.info(3,"Removing {} from bigger primary {}".format(adj, bigger))
bigger.grids.remove(adj)
elif adj in smaller.grids:
debug.info(3,"Removing {} from smaller primary {}".format(adj, smaller))
smaller.grids.remove(adj)
@ -359,17 +346,6 @@ class router(router_tech):
self.set_blockages(blockage_grids,False)
# 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):
"""
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
ll = ll.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]
def convert_pin_to_tracks(self, pin_name, pin, expansion=0):
@ -515,36 +486,24 @@ class router(router_tech):
# scale the size bigger to include neaby tracks
ll=ll.scale(self.track_factor).floor()
ur=ur.scale(self.track_factor).ceil()
#print(pin)
# Keep tabs on tracks with sufficient and insufficient overlap
sufficient_list = set()
insufficient_list = set()
zindex=self.get_zindex(pin.layer_num)
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):
(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:
sufficient_list.update([full_overlap])
if 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
sufficient_list.difference_update(self.blocked_grids)
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()
# Return all grids with any potential overlap (sufficient or not)
return (sufficient_list,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.
"""
#print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap
best_coord = None
best_overlap = -math.inf
@ -589,7 +547,6 @@ class router(router_tech):
Get a grid cell that is the furthest from the blocked grids.
"""
#print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap
best_coord = None
best_dist = math.inf
@ -606,7 +563,6 @@ class router(router_tech):
Given a pin and a list of grid cells (probably non-overlapping),
return the nearest grid cell (center to center).
"""
#print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap
best_coord = None
best_dist = math.inf
@ -622,30 +578,32 @@ class router(router_tech):
def convert_pin_coord_to_tracks(self, pin, coord):
"""
Given a pin and a track coordinate, determine if the pin overlaps enough.
If it does, add additional metal to make the pin "on grid".
If it doesn't, add it to the blocked grid list.
Return all tracks that an inflated pin overlaps
"""
(width, spacing) = self.get_layer_width_space(coord.z)
# This is the rectangle if we put a pin in the center of the track
track_pin = self.convert_track_to_pin(coord)
# 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)
overlap_length = pin.overlap_length(track_pin)
debug.info(3,"Check overlap: {0} {1} . {2} = {3}".format(coord, pin.rect, track_pin, overlap_length))
# If it overlaps by more than the min width DRC, we can just use the track
if overlap_length==math.inf or snap_val_to_grid(overlap_length) >= snap_val_to_grid(width):
debug.info(3," Overlap: {0} >? {1}".format(overlap_length,spacing))
return (coord, None)
# Otherwise, keep track of the partial overlap grids in case we need to patch it later.
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))
# If it overlaps with the pin, it is sufficient
if overlap_length==math.inf or overlap_length > 0:
debug.info(2," Overlap: {0} >? {1}".format(overlap_length,0))
return (coord,None)
# If it overlaps with the inflated pin, it is partial
elif inflated_overlap_length==math.inf or inflated_overlap_length > 0:
debug.info(2," Partial overlap: {0} >? {1}".format(inflated_overlap_length,0))
return (None,coord)
else:
debug.info(3," Partial/no overlap: {0} >? {1}".format(overlap_length,spacing))
return (None, coord)
debug.info(2," No overlap: {0} {1}".format(overlap_length,0))
return (None,None)
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
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
x = track.x*self.track_width - 0.5*self.track_width + space
y = track.y*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 + 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 - space
y = track.y*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 - 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 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):
"""
Convert a grid point into a rectangle shape that occupies the entire centered
@ -686,6 +653,23 @@ class router(router_tech):
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):
"""
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.add_enclosure(self.cell)
#self.write_debug_gds("pin_debug.gds", False)
def add_source(self, pin_name):
"""
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))
# Keep track of path for future blockages
#path.set_blocked()
# This is marked for debug
path.set_path()
@ -877,8 +856,6 @@ class router(router_tech):
Enclose the tracks from ll to ur in a single rectangle that meets
the track DRC rules.
"""
# Get the layer information
(width, space) = self.get_layer_width_space(zindex)
layer = self.get_layer(zindex)
# This finds the pin shape enclosed by the track with DRC spacing on the sides
@ -886,44 +863,10 @@ class router(router_tech):
(abs_ll,unused) = pin.rect
pin = self.convert_track_to_pin(ur)
(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)
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):
"""
@ -963,8 +906,7 @@ class router(router_tech):
self.add_route(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(inflated_path)
self.path_blockages.append(path_set)
else:
self.write_debug_gds("failed_route.gds")
# clean up so we can try a reroute

View File

@ -3,48 +3,62 @@ from contact import contact
from pin_group import pin_group
from vector import vector
import debug
import math
class router_tech:
"""
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
is always horizontal, middle is via, and last is always
vertical.
"""
self.layers = 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
via_connect = contact(self.layers, (1, 1))
self.max_via_size = max(via_connect.width,via_connect.height)
self.vert_layer_minwidth = drc("minwidth_{0}".format(self.vert_layer_name))
self.vert_layer_spacing = drc(str(self.vert_layer_name)+"_to_"+str(self.vert_layer_name))
max_via_size = max(via_connect.width,via_connect.height)
self.horiz_layer_number = layer[self.horiz_layer_name]
self.vert_layer_number = layer[self.vert_layer_name]
self.horiz_layer_minwidth = drc("minwidth_{0}".format(self.horiz_layer_name))
self.horiz_layer_spacing = drc(str(self.horiz_layer_name)+"_to_"+str(self.horiz_layer_name))
self.horiz_layer_number = layer[self.horiz_layer_name]
self.horiz_track_width = self.max_via_size + self.horiz_layer_spacing
self.vert_track_width = self.max_via_size + self.vert_layer_spacing
if self.rail_track_width>1:
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_supply_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_supply_layer_width_space(0)
# For supplies, we will make the wire wider than the vias
self.vert_layer_minwidth = max(self.vert_layer_minwidth, max_via_size)
self.horiz_layer_minwidth = max(self.horiz_layer_minwidth, max_via_size)
self.horiz_track_width = self.horiz_layer_minwidth + self.horiz_layer_spacing
self.vert_track_width = self.vert_layer_minwidth + self.vert_layer_spacing
else:
(self.vert_layer_minwidth, self.vert_layer_spacing) = self.get_layer_width_space(1)
(self.horiz_layer_minwidth, self.horiz_layer_spacing) = self.get_layer_width_space(0)
self.horiz_track_width = max_via_size + self.horiz_layer_spacing
self.vert_track_width = max_via_size + self.vert_layer_spacing
# We'll keep horizontal and vertical tracks the same for simplicity.
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_factor = vector([1/self.track_width] * 2)
debug.info(2,"Track factor: {0}".format(self.track_factor))
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
self.layer_widths = [self.track_width - self.horiz_layer_spacing, 1, self.track_width - self.vert_layer_spacing]
debug.info(2,"Track factor: {}".format(self.track_factor))
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
self.layer_widths = [self.track_wire, 1, self.track_wire]
def get_zindex(self,layer_num):
if layer_num==self.horiz_layer_number:
return 0
@ -76,4 +90,24 @@ class router_tech:
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
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
self.supply_rails = {}
self.supply_rail_wires = {}
# This is the same as above but as a sigle set for the all the rails
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.
self.create_routing_grid()
# Compute the grid dimensions
self.compute_supply_rail_dimensions()
# Get the pin shapes
#start_time = datetime.now()
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
#start_time = datetime.now()
# Block everything
self.prepare_blockages(self.gnd_name)
# Determine the rail locations
@ -85,23 +77,15 @@ class supply_router(router):
self.prepare_blockages(self.vdd_name)
# Determine the rail locations
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(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 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(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)
return True
@ -116,10 +100,8 @@ class supply_router(router):
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name))
# These are the wire tracks
wire_tracks = self.supply_rail_wire_tracks[pin_name]
# These are the wire and space tracks
supply_tracks = self.supply_rail_tracks[pin_name]
wire_tracks = self.supply_rail_tracks[pin_name]
routed_count=0
for pg in self.pin_groups[pin_name]:
if pg.is_routed():
continue
@ -127,17 +109,15 @@ class supply_router(router):
# First, check if we just overlap, if so, we are done.
overlap_grids = wire_tracks & pg.grids
if len(overlap_grids)>0:
routed_count += 1
pg.set_routed()
continue
# Else, if we overlap some of the space track, we can patch it with an enclosure
common_set = supply_tracks & pg.grids
if len(common_set)>0:
pg.create_simple_overlap_enclosure(common_set)
pg.add_enclosure(self.cell)
# Else, if we overlap some of the space track, we can patch it with an enclosure
#pg.create_simple_overlap_enclosure(pg.grids)
#pg.add_enclosure(self.cell)
debug.info(1,"Routed {} simple overlap pins".format(routed_count))
def finalize_supply_rails(self, name):
"""
@ -146,7 +126,7 @@ class supply_router(router):
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()
via_areas = []
@ -186,8 +166,8 @@ class supply_router(router):
# the indices to determine a rail is connected to another
# the overlap area for placement of a via
overlap = new_r1 & new_r2
if len(overlap) >= self.supply_rail_wire_width**2:
debug.info(3,"Via overlap {0} {1} {2}".format(len(overlap),self.supply_rail_wire_width**2,overlap))
if len(overlap) >= 1:
debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap))
connections.update([i1,i2])
via_areas.append(overlap)
@ -196,7 +176,7 @@ class supply_router(router):
ll = grid_utils.get_lower_left(area)
ur = grid_utils.get_upper_right(area)
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
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])
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur))
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.
# 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)
ur = grid_utils.get_upper_right(rail)
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))
self.cell.add_layout_pin(text=name,
layer=pin.layer,
@ -234,43 +213,6 @@ class supply_router(router):
width=pin.width(),
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):
"""
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_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
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
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 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)
if not added_rail:
# Just seed with the next one
@ -298,12 +244,12 @@ class supply_router(router):
wave = added_rail.neighbor(direction.EAST)
# Vertical supply rails
max_offset = self.rg.ur.x
for offset in range(start_offset, self.max_xoffset, 2*self.supply_rail_width):
start_offset = min_xoffset + supply_number
for offset in range(start_offset, max_xoffset, 2):
# 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 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)
if not added_rail:
# Just seed with the next one
@ -378,11 +324,6 @@ class supply_router(router):
if len(wave_path)>=4*self.rail_track_width:
grid_set = wave_path.get_grids()
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 False
@ -417,10 +358,6 @@ class supply_router(router):
rail_set.update(rail)
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):
@ -430,8 +367,8 @@ class supply_router(router):
"""
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,
remaining_components))
debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name,
remaining_components))
for index,pg in enumerate(self.pin_groups[pin_name]):
if pg.is_routed():
@ -465,7 +402,7 @@ class supply_router(router):
"""
debug.info(4,"Add supply rail target {}".format(pin_name))
# 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
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:
from control_logic import control_logic
cell = control_logic(16)
layer_stack =("metal3","via3","metal4")
rtr=router(layer_stack, cell)
self.assertTrue(rtr.route())
else:
from sram import sram
from sram_config import sram_config
@ -33,9 +36,6 @@ class no_blockages_test(openram_test):
sram = sram(c, "sram1")
cell = sram.s
layer_stack =("metal3","via3","metal4")
rtr=router(layer_stack, cell)
self.assertTrue(rtr.route())
self.local_check(cell,True)
# fails if there are any DRC errors on any cells

View File

@ -14,7 +14,6 @@ class sram():
"""
def __init__(self, sram_config, name):
sram_config.compute_sizes()
sram_config.set_local_config(self)
# reset the static duplicate name checker for unit tests
@ -34,8 +33,6 @@ class sram():
from sram_1bank import sram_1bank as sram
elif self.num_banks == 2:
from sram_2bank import sram_2bank as sram
elif self.num_banks == 4:
from sram_4bank import sram_4bank as sram
else:
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 bank import bank
from contact import m2m3
from dff_buf_array import dff_buf_array
from dff_array import dff_array
@ -21,11 +22,6 @@ class sram_1bank(sram_base):
def __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):
"""
This adds the modules for a single bank SRAM with control
@ -61,15 +57,18 @@ class sram_1bank(sram_base):
row_addr_pos = [None]*len(self.all_ports)
col_addr_pos = [None]*len(self.all_ports)
data_pos = [None]*len(self.all_ports)
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
data_gap = self.m2_pitch*(self.word_size+1)
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
# The M1 pitch is for supply rail spacings
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch
# Port 0
port = 0
# 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,
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])
# 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
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,
-data_gap - self.col_addr_dff_insts[port].height)
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-max_gap_size - self.col_addr_dff_insts[port].height)
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
@ -92,16 +91,18 @@ class sram_1bank(sram_base):
# sense amps.
if port in self.write_ports:
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])
if len(self.all_ports)>1:
# Port 1
port = 1
# 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,
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")
# 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
if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.central_bus_width,
self.bank_inst.uy() + data_gap + self.col_addr_dff_insts[port].height)
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
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")
# 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.
if port in self.write_ports:
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")
@ -178,27 +179,11 @@ class sram_1bank(sram_base):
# Connect all of these clock pins to the clock in the central bus
# This is something like a "spine" clock distribution. The two spines
# are clk_buf and clk_buf_bar
control_clk_buf_pin = self.control_logic_insts[port].get_pin("clk_buf")
control_clk_buf_pos = control_clk_buf_pin.center()
bank_clk_buf_pin = self.bank_inst.get_pin("clk_buf{}".format(port))
bank_clk_buf_pos = bank_clk_buf_pin.center()
bank_clk_buf_bar_pin = self.bank_inst.get_pin("clk_buf_bar{}".format(port))
bank_clk_buf_bar_pos = bank_clk_buf_bar_pin.center()
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[dff_clk_pos, mid_pos, bank_clk_buf_pos])
if port in self.write_ports:
data_dff_clk_pin = self.data_dff_insts[port].get_pin("clk")
data_dff_clk_pos = data_dff_clk_pin.center()
mid_pos = vector(bank_clk_buf_pos.x, data_dff_clk_pos.y)
self.add_wire(("metal3","via2","metal2"),[data_dff_clk_pos, mid_pos, bank_clk_buf_pos])
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
# 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")
if port%2:
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()
mid1_pos = vector(self.row_addr_dff_insts[port].rx() + self.m2_pitch,
row_addr_clk_pos.y)
mid2_pos = vector(mid1_pos.x,
control_clk_buf_pos.y)
# Note, the via to the control logic is taken care of when we route
# the control logic to the bank
self.add_wire(("metal3","via2","metal2"),[row_addr_clk_pos, mid1_pos, mid2_pos, control_clk_buf_pos])
# This is the steiner point where the net branches out
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
self.add_path("metal1", [control_clk_buf_pos, clk_steiner_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):
""" Route the outputs from the control logic module """
for port in self.all_ports:
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)
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
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 """
# This is where the channel will start (y-dimension at least)
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_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
bank_names = ["din{0}_{1}".format(port,x) for x in range(self.word_size)]
route_map = list(zip(bank_names, dff_names))
dff_pins = {key: self.data_dff_insts[port].get_pin(key) for key in dff_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.
all_pins = {**dff_pins, **bank_pins}
self.create_horizontal_channel_route(route_map, all_pins, offset)
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
route_map = list(zip(bank_pins, dff_pins))
self.create_horizontal_channel_route(route_map, 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 getpass
import debug
from datetime import datetime
from importlib import reload
from vector import vector
from globals import OPTS, print_time
@ -63,37 +64,52 @@ class sram_base(design):
def create_netlist(self):
""" Netlist creation """
start_time = datetime.now()
# Must create the control logic before pins to get the pins
self.add_modules()
self.add_pins()
self.create_modules()
# This is for the lib file if we don't create layout
self.width=0
self.height=0
if not OPTS.is_unit_test:
print_time("Submodules",datetime.now(), start_time)
def create_layout(self):
""" Layout creation """
start_time = datetime.now()
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_supplies()
if not OPTS.is_unit_test:
print_time("Routing",datetime.now(), start_time)
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
# Must be done after offsetting lower-left
self.route_supplies()
highest_coord = self.find_highest_coords()
self.width = highest_coord[0]
self.height = highest_coord[1]
start_time = datetime.now()
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):
""" 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:
self.control_bus_names = []
for port in self.all_ports:
self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)]
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"):
self.control_bus_names[port].append("w_en{}".format(port))
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"):
self.control_bus_names[port].append("s_en{}".format(port))
self.control_bus_names[port] = ["clk_buf{}".format(port)]
wen = "w_en{}".format(port)
sen = "s_en{}".format(port)
pen = "p_en_bar{}".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",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
@ -287,11 +308,12 @@ class sram_base(design):
temp.append("bank_sel{0}[{1}]".format(port,bank_num))
for port in self.read_ports:
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:
temp.append("w_en{0}".format(port))
for port in self.all_ports:
temp.append("clk_buf_bar{0}".format(port))
temp.append("clk_buf{0}".format(port))
temp.append("wl_en{0}".format(port))
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
@ -403,16 +425,21 @@ class sram_base(design):
mod = self.control_logic_r
insts.append(self.add_inst(name="control{}".format(port), mod=mod))
# Inputs
temp = ["csb{}".format(port)]
if port in self.readwrite_ports:
temp.append("web{}".format(port))
temp.append("clk{}".format(port))
# Ouputs
if port in self.read_ports:
temp.append("s_en{}".format(port))
if port in self.write_ports:
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)
return insts

View File

@ -14,7 +14,7 @@ class sram_config:
# This will get over-written when we determine the organization
self.words_per_row = None
# Move the module names to this?
self.compute_sizes()
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.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
self.num_cols = int(self.words_per_row*self.word_size)
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.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):
"""
@ -74,10 +87,14 @@ class sram_config:
if tentative_num_cols < 1.5*word_size:
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
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):
"""

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Run a regression test on a dff_inv.
Run a regression test on a pand2 cell
"""
import unittest
@ -11,19 +11,22 @@ import globals
from globals import OPTS
import debug
class dff_inv_test(openram_test):
class pand2_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import dff_inv
global verify
import verify
debug.info(2, "Testing dff_inv 4x")
a = dff_inv.dff_inv(4)
import pand2
debug.info(2, "Testing pand2 gate 4x")
a = pand2.pand2(4)
self.local_check(a)
globals.end_openram()
# run the test from the command line
# instantiate a copdsay of the class to actually run the test
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
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.words_per_row=1
c.recompute_sizes()
debug.info(1, "No column mux")
a = bank(c, name="bank1_multi")
self.local_check(a)
c.num_words=32
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = bank(c, name="bank2_multi")
self.local_check(a)
c.num_words=64
c.words_per_row=4
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = bank(c, name="bank3_multi")
self.local_check(a)
@ -43,6 +46,7 @@ class multi_bank_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
c.recompute_sizes()
debug.info(1, "Eight way column mux")
a = bank(c, name="bank4_multi")
self.local_check(a)

View File

@ -31,6 +31,7 @@ class psingle_bank_test(openram_test):
num_words=16)
c.words_per_row=1
c.recompute_sizes()
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)
a = bank(c, name=name)
@ -38,6 +39,7 @@ class psingle_bank_test(openram_test):
c.num_words=32
c.words_per_row=2
c.recompute_sizes()
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)
a = bank(c, name=name)
@ -45,6 +47,7 @@ class psingle_bank_test(openram_test):
c.num_words=64
c.words_per_row=4
c.recompute_sizes()
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)
a = bank(c, name=name)
@ -53,6 +56,7 @@ class psingle_bank_test(openram_test):
c.word_size=2
c.num_words=128
c.words_per_row=8
c.recompute_sizes()
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)
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
import debug
#@unittest.skip("SKIPPING 20_psram_1bank_test, multiport layout not complete")
class psram_1bank_2mux_test(openram_test):
#@unittest.skip("SKIPPING 20_psram_1bank_2mux_1w_1r_test, odd supply routing error")
class psram_1bank_2mux_1w_1r_test(openram_test):
def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
@ -30,7 +30,14 @@ class psram_1bank_2mux_test(openram_test):
num_banks=1)
c.num_words=32
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")
self.local_check(a, final_verification=True)

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
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):
def runTest(self):
@ -31,7 +31,14 @@ class psram_1bank_2mux_test(openram_test):
num_banks=1)
c.num_words=32
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")
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)
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")
self.local_check(a, final_verification=True)

View File

@ -23,7 +23,14 @@ class sram_1bank_2mux_test(openram_test):
num_banks=1)
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")
self.local_check(a, final_verification=True)

View File

@ -23,7 +23,14 @@ class sram_1bank_4mux_test(openram_test):
num_banks=1)
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")
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)
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")
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)
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")
self.local_check(a, final_verification=True)

View File

@ -23,7 +23,14 @@ class sram_1bank_nomux_test(openram_test):
num_banks=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")
self.local_check(a, final_verification=True)

View File

@ -23,18 +23,21 @@ class sram_2bank_test(openram_test):
num_banks=2)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Two bank, no column mux with control logic")
a = sram(c, "sram1")
self.local_check(a, final_verification=True)
c.num_words=64
c.words_per_row=2
c.recompute_sizes()
debug.info(1, "Two bank two way column mux with control logic")
a = sram(c, "sram2")
self.local_check(a, final_verification=True)
c.num_words=128
c.words_per_row=4
c.recompute_sizes()
debug.info(1, "Two bank, four way column mux with control logic")
a = sram(c, "sram3")
self.local_check(a, final_verification=True)
@ -42,6 +45,7 @@ class sram_2bank_test(openram_test):
c.word_size=2
c.num_words=256
c.words_per_row=8
c.recompute_sizes()
debug.info(1, "Two bank, eight way column mux with control logic")
a = sram(c, "sram4")
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_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram(c, name="sram1")
@ -49,29 +50,28 @@ class timing_sram_test(openram_test):
#Combine info about port into all data
data.update(port_data[0])
#Assumes single rw port (6t sram)
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [2.5829000000000004],
'delay_lh': [0.2255964],
'leakage_power': 0.0019498999999999996,
golden_data = {'delay_hl': [2.6232],
'delay_lh': [0.2775342],
'leakage_power': 0.0020258999999999997,
'min_period': 4.844,
'read0_power': [0.055371399999999994],
'read1_power': [0.0520225],
'slew_hl': [0.0794261],
'slew_lh': [0.0236264],
'write0_power': [0.06545659999999999],
'write1_power': [0.057846299999999996]}
'read0_power': [0.0557804],
'read1_power': [0.0525619],
'slew_hl': [0.1082014],
'slew_lh': [0.0238257],
'write0_power': [0.0456528],
'write1_power': [0.0442747]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [3.452],
'delay_lh': [1.3792000000000002],
'leakage_power': 0.0257065,
'min_period': 4.688,
'read0_power': [15.0755],
'read1_power': [14.4526],
'slew_hl': [0.6137363],
'slew_lh': [0.3381045],
'write0_power': [16.9203],
'write1_power': [15.367]}
golden_data = {'delay_hl': [6.079300000000001],
'delay_lh': [1.7767000000000002],
'leakage_power': 0.026282499999999997,
'min_period': 9.375,
'read0_power': [6.5802],
'read1_power': [6.2815],
'slew_hl': [0.7396921999999999],
'slew_lh': [0.3397355],
'write0_power': [5.7337],
'write1_power': [5.8691]}
else:
self.assertTrue(False) # other techs fail
# 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_banks=1)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
s = sram(c, name="sram1")
@ -50,27 +51,27 @@ class timing_sram_test(openram_test):
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [2.584251],
'delay_lh': [0.22870469999999998],
'leakage_power': 0.0009567935,
golden_data = {'delay_hl': [2.625351],
'delay_lh': [0.28080869999999997],
'leakage_power': 0.001040682,
'min_period': 4.844,
'read0_power': [0.0547588],
'read1_power': [0.051159970000000006],
'slew_hl': [0.08164099999999999],
'slew_lh': [0.025474979999999998],
'write0_power': [0.06513271999999999],
'write1_power': [0.058057000000000004]}
'read0_power': [0.0553667],
'read1_power': [0.05177618],
'slew_hl': [0.1099853],
'slew_lh': [0.02568626],
'write0_power': [0.04517803],
'write1_power': [0.04449207]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [3.644147],
'delay_lh': [1.629815],
'leakage_power': 0.001542964,
'min_period': 4.688,
'read0_power': [16.28732],
'read1_power': [15.75155],
'slew_hl': [0.6722473],
'slew_lh': [0.3386347],
'write0_power': [18.545450000000002],
'write1_power': [16.81084]}
golden_data = {'delay_hl': [6.45408],
'delay_lh': [2.0787519999999997],
'leakage_power': 0.001177846,
'min_period': 9.688,
'read0_power': [7.088419],
'read1_power': [6.824107000000001],
'slew_hl': [0.7980976999999999],
'slew_lh': [0.3393389],
'write0_power': [5.982207],
'write1_power': [6.28866]}
else:
self.assertTrue(False) # other techs fail

View File

@ -11,8 +11,8 @@ import globals
from globals import OPTS
import debug
#@unittest.skip("SKIPPING 22_psram_1bank_2mux_func_test")
class psram_1bank_2mux_func_test(openram_test):
@unittest.skip("SKIPPING 22_psram_1bank_2mux_1rw_1r_1w_func_test, third port reads are broken?")
class psram_1bank_2mux_1rw_1r_1w_func_test(openram_test):
def runTest(self):
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_banks=1)
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.num_words,
c.words_per_row,
c.num_banks))
c.recompute_sizes()
debug.info(1, "Functional 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))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -11,7 +11,7 @@ import globals
from globals import OPTS
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):
def runTest(self):
@ -35,10 +35,14 @@ class psram_1bank_4mux_func_test(openram_test):
num_words=256,
num_banks=1)
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.num_words,
c.words_per_row,
c.num_banks))
c.recompute_sizes()
debug.info(1, "Functional 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))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -35,10 +35,14 @@ class psram_1bank_8mux_func_test(openram_test):
num_words=256,
num_banks=1)
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.num_words,
c.words_per_row,
c.num_banks))
c.recompute_sizes()
debug.info(1, "Functional 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))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -35,10 +35,14 @@ class psram_1bank_nomux_func_test(openram_test):
num_words=32,
num_banks=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.num_words,
c.words_per_row,
c.num_banks))
c.recompute_sizes()
debug.info(1, "Functional 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))
s = sram(c, name="sram")
tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice)

View File

@ -30,6 +30,7 @@ class sram_1bank_2mux_func_test(openram_test):
num_words=64,
num_banks=1)
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,
c.num_words,
c.words_per_row,

View File

@ -30,6 +30,7 @@ class sram_1bank_4mux_func_test(openram_test):
num_words=256,
num_banks=1)
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,
c.num_words,
c.words_per_row,

View File

@ -33,6 +33,7 @@ class sram_1bank_8mux_func_test(openram_test):
num_words=256,
num_banks=1)
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,
c.num_words,
c.words_per_row,

View File

@ -30,6 +30,7 @@ class sram_1bank_nomux_func_test(openram_test):
num_words=32,
num_banks=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,
c.num_words,
c.words_per_row,

View File

@ -35,6 +35,7 @@ class psram_1bank_nomux_func_test(openram_test):
num_words=32,
num_banks=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,
c.num_words,
c.words_per_row,

View File

@ -23,6 +23,7 @@ class lib_test(openram_test):
num_words=16,
num_banks=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")
s = sram(c, "sram_2_16_1_{0}".format(OPTS.tech_name))
tempspice = OPTS.openram_temp + "temp.sp"

View File

@ -32,6 +32,7 @@ class lib_test(openram_test):
num_words=16,
num_banks=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")
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_banks=1)
c.words_per_row=1
c.recompute_sizes()
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))

View File

@ -23,6 +23,7 @@ class lef_test(openram_test):
num_words=16,
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
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))

View File

@ -22,6 +22,7 @@ class verilog_test(openram_test):
num_words=16,
num_banks=1)
c.words_per_row=1
c.recompute_sizes()
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))

View File

@ -39,7 +39,7 @@ class worst_case_timing_sram_test(openram_test):
num_words=num_words,
num_banks=num_banks)
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(
word_size, num_words, num_banks))
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"))
cmd = "python3 {0}/openram.py -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(OPENRAM_HOME,
out_file,
out_path,
verbosity,
OPTS.tech_name,
out_path)
# Always perform code coverage
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} config_20_{4}.py 2>&1 > {5}/output.log".format(exe_name,
out_file,
out_path,
verbosity,
OPTS.tech_name,
out_path)
debug.info(1, cmd)
os.system(cmd)

View File

@ -2,34 +2,35 @@
.SUBCKT write_driver din bl br en vdd gnd
**** Inverter to conver Data_in to data_in_bar ******
M_1 din_bar din gnd gnd n W='1.2*1u' L=0.6u
M_2 din_bar din vdd vdd p W='2.1*1u' L=0.6u
* din_bar = inv(din)
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 ******
M_3 din_bar_gated en net_7 gnd n W='2.1*1u' L=0.6u
M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u
M_5 din_bar_gated en vdd vdd p W='2.1*1u' L=0.6u
M_6 din_bar_gated din vdd vdd p W='2.1*1u' L=0.6u
M_7 net_1 din_bar_gated vdd vdd p W='2.1*1u' L=0.6u
M_8 net_1 din_bar_gated gnd gnd n W='1.2*1u' L=0.6u
* din_bar_gated = nand(en, din)
M_3 din_bar_gated en net_7 gnd n W=2.1u L=0.6u
M_4 net_7 din gnd gnd n W=2.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 din_bar_gated_bar din_bar_gated vdd vdd p W=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******
M_9 din_gated en vdd vdd p W='2.1*1u' L=0.6u
M_10 din_gated en net_8 gnd n W='2.1*1u' L=0.6u
M_11 net_8 din_bar gnd gnd n W='2.1*1u' L=0.6u
M_12 din_gated din_bar vdd vdd p W='2.1*1u' L=0.6u
M_13 net_6 din_gated vdd vdd p W='2.1*1u' L=0.6u
M_14 net_6 din_gated gnd gnd n W='1.2*1u' L=0.6u
* din_gated = nand(en, din_bar)
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.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.1u L=0.6u
* din_gated_bar = inv(din_gated)
M_13 din_gated_bar din_gated vdd vdd p W=2.1u L=0.6u
M_14 din_gated_bar din_gated gnd gnd n W=1.2u L=0.6u
************************************************
M_15 bl net_6 net_5 gnd n W='3.6*1u' L=0.6u
M_16 br net_1 net_5 gnd n W='3.6*1u' L=0.6u
M_17 net_5 en gnd gnd n W='3.6*1u' L=0.6u
* pull down with en enable
M_15 bl din_gated_bar net_5 gnd n W=3.6u 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.6u L=0.6u

View File

@ -1,23 +1,37 @@
*********************** Write_Driver ******************************
.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
M1001 br a_16_500# a_44_708# gnd n w=2.4u l=0.4u
M1002 a_44_708# en gnd gnd n w=2.4u l=0.4u
M1003 gnd a_8_284# a_16_500# gnd n w=0.8u 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
**** Inverter to conver Data_in to data_in_bar ******
* din_bar = inv(din)
M_1 din_bar din gnd gnd n W=0.8u L=0.4u
M_2 din_bar 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["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["nom_supply_voltage"] = 5.0 # Nominal supply voltage in [Volts]
spice["rise_time"] = 0.05 # rise time in [Nano-seconds]