Merge remote-tracking branch 'private/dev' into dev

This commit is contained in:
mrg 2022-08-30 12:58:35 -07:00
commit 2c8a52f32d
41 changed files with 2240 additions and 2010 deletions

3
.gitignore vendored
View File

@ -13,3 +13,6 @@ technology/freepdk45/ncsu_basekit
technology/sky130/*_lib
technology/sky130/tech/.magicrc
.idea
compiler/tests/results/
sky*/
open_pdks/

View File

@ -78,7 +78,7 @@ class design(hierarchy_design):
for pin_name in self.pins:
pins = self.get_pins(pin_name)
for pin in pins:
print(pin_name, pin)
debug.info(0, "{0} {1}".format(pin_name, pin))
def setup_multiport_constants(self):
"""

View File

@ -141,30 +141,28 @@ class layout():
layout.active_space)
# These are for debugging previous manual rules
if False:
print("poly_width", layout.poly_width)
print("poly_space", layout.poly_space)
print("m1_width", layout.m1_width)
print("m1_space", layout.m1_space)
print("m2_width", layout.m2_width)
print("m2_space", layout.m2_space)
print("m3_width", layout.m3_width)
print("m3_space", layout.m3_space)
print("m4_width", layout.m4_width)
print("m4_space", layout.m4_space)
print("active_width", layout.active_width)
print("active_space", layout.active_space)
print("contact_width", layout.contact_width)
print("poly_to_active", layout.poly_to_active)
print("poly_extend_active", layout.poly_extend_active)
print("poly_to_contact", layout.poly_to_contact)
print("active_contact_to_gate", layout.active_contact_to_gate)
print("poly_contact_to_gate", layout.poly_contact_to_gate)
print("well_enclose_active", layout.well_enclose_active)
print("implant_enclose_active", layout.implant_enclose_active)
print("implant_space", layout.implant_space)
import sys
sys.exit(1)
level=99
debug.info(level, "poly_width".format(layout.poly_width))
debug.info(level, "poly_space".format(layout.poly_space))
debug.info(level, "m1_width".format(layout.m1_width))
debug.info(level, "m1_space".format(layout.m1_space))
debug.info(level, "m2_width".format(layout.m2_width))
debug.info(level, "m2_space".format(layout.m2_space))
debug.info(level, "m3_width".format(layout.m3_width))
debug.info(level, "m3_space".format(layout.m3_space))
debug.info(level, "m4_width".format(layout.m4_width))
debug.info(level, "m4_space".format(layout.m4_space))
debug.info(level, "active_width".format(layout.active_width))
debug.info(level, "active_space".format(layout.active_space))
debug.info(level, "contact_width".format(layout.contact_width))
debug.info(level, "poly_to_active".format(layout.poly_to_active))
debug.info(level, "poly_extend_active".format(layout.poly_extend_active))
debug.info(level, "poly_to_contact".format(layout.poly_to_contact))
debug.info(level, "active_contact_to_gate".format(layout.active_contact_to_gate))
debug.info(level, "poly_contact_to_gate".format(layout.poly_contact_to_gate))
debug.info(level, "well_enclose_active".format(layout.well_enclose_active))
debug.info(level, "implant_enclose_active".format(layout.implant_enclose_active))
debug.info(level, "implant_space".format(layout.implant_space))
@classmethod
def setup_layer_constants(layout):
@ -202,21 +200,19 @@ class layout():
"{}_nonpref_pitch".format(layer_id),
layout.compute_pitch(layer_id, False))
if False:
for name in tech_layer_indices:
if name == "active":
continue
try:
print("{0} width {1} space {2}".format(name,
getattr(layout, "{}_width".format(name)),
getattr(layout, "{}_space".format(name))))
level=99
for name in tech_layer_indices:
if name == "active":
continue
try:
debug.info(level, "{0} width {1} space {2}".format(name,
getattr(layout, "{}_width".format(name)),
getattr(layout, "{}_space".format(name))))
print("pitch {0} nonpref {1}".format(getattr(layout, "{}_pitch".format(name)),
getattr(layout, "{}_nonpref_pitch".format(name))))
except AttributeError:
pass
import sys
sys.exit(1)
debug.info(level, "pitch {0} nonpref {1}".format(getattr(layout, "{}_pitch".format(name)),
getattr(layout, "{}_nonpref_pitch".format(name))))
except AttributeError:
pass
@staticmethod
def compute_pitch(layer, preferred=True):

View File

@ -24,7 +24,7 @@ class verilog:
self.vf.write("// OpenRAM SRAM model\n")
self.vf.write("// Words: {0}\n".format(self.num_words))
self.vf.write("// Word size: {0}\n".format(self.word_size))
if self.write_size:
if self.write_size != self.word_size:
self.vf.write("// Write size: {0}\n\n".format(self.write_size))
else:
self.vf.write("\n")
@ -38,7 +38,10 @@ class verilog:
except KeyError:
self.gnd_name = "gnd"
self.vf.write("module {0}(\n".format(self.name))
if self.num_banks > 1:
self.vf.write("module {0}(\n".format(self.name))
else:
self.vf.write("module {0}(\n".format(self.name))
self.vf.write("`ifdef USE_POWER_PINS\n")
self.vf.write(" {},\n".format(self.vdd_name))
self.vf.write(" {},\n".format(self.gnd_name))
@ -53,14 +56,14 @@ class verilog:
self.vf.write("// Port {0}: W\n".format(port))
if port in self.readwrite_ports:
self.vf.write(" clk{0},csb{0},web{0},".format(port))
if self.write_size:
if self.write_size != self.word_size:
self.vf.write("wmask{},".format(port))
if self.num_spare_cols > 0:
self.vf.write("spare_wen{0},".format(port))
self.vf.write("addr{0},din{0},dout{0}".format(port))
elif port in self.write_ports:
self.vf.write(" clk{0},csb{0},".format(port))
if self.write_size:
if self.write_size != self.word_size:
self.vf.write("wmask{},".format(port))
if self.num_spare_cols > 0:
self.vf.write("spare_wen{0},".format(port))
@ -72,11 +75,11 @@ class verilog:
self.vf.write(",\n")
self.vf.write("\n );\n\n")
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
self.vf.write(" parameter NUM_WMASKS = {0} ;\n".format(self.num_wmasks))
self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size + self.num_spare_cols))
self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.addr_size))
self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(self.bank_addr_size))
self.vf.write(" parameter RAM_DEPTH = 1 << ADDR_WIDTH;\n")
self.vf.write(" // FIXME: This delay is arbitrary.\n")
self.vf.write(" parameter DELAY = 3 ;\n")
@ -125,7 +128,7 @@ class verilog:
if port in self.readwrite_ports:
self.vf.write(" reg web{0}_reg;\n".format(port))
if port in self.write_ports:
if self.write_size:
if self.write_size != self.word_size:
self.vf.write(" reg [NUM_WMASKS-1:0] wmask{0}_reg;\n".format(port))
if self.num_spare_cols > 1:
self.vf.write(" reg [{1}:0] spare_wen{0}_reg;".format(port, self.num_spare_cols - 1))
@ -149,7 +152,7 @@ class verilog:
if port in self.readwrite_ports:
self.vf.write(" web{0}_reg = web{0};\n".format(port))
if port in self.write_ports:
if self.write_size:
if self.write_size != self.word_size:
self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port))
if self.num_spare_cols:
self.vf.write(" spare_wen{0}_reg = spare_wen{0};\n".format(port))
@ -169,13 +172,13 @@ class verilog:
self.vf.write(" $display($time,\" Reading %m addr{0}=%b dout{0}=%b\",addr{0}_reg,mem[addr{0}_reg]);\n".format(port))
if port in self.readwrite_ports:
self.vf.write(" if ( !csb{0}_reg && !web{0}_reg && VERBOSE )\n".format(port))
if self.write_size:
if self.write_size != self.word_size:
self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b wmask{0}=%b\",addr{0}_reg,din{0}_reg,wmask{0}_reg);\n".format(port))
else:
self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b\",addr{0}_reg,din{0}_reg);\n".format(port))
elif port in self.write_ports:
self.vf.write(" if ( !csb{0}_reg && VERBOSE )\n".format(port))
if self.write_size:
if self.write_size != self.word_size:
self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b wmask{0}=%b\",addr{0}_reg,din{0}_reg,wmask{0}_reg);\n".format(port))
else:
self.vf.write(" $display($time,\" Writing %m addr{0}=%b din{0}=%b\",addr{0}_reg,din{0}_reg);\n".format(port))
@ -193,7 +196,7 @@ class verilog:
self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port))
if port in self.write_ports:
if self.write_size:
if self.write_size != self.word_size:
self.vf.write(" input [NUM_WMASKS-1:0] wmask{0}; // write mask\n".format(port))
if self.num_spare_cols == 1:
self.vf.write(" input spare_wen{0}; // spare mask\n".format(port))
@ -218,7 +221,7 @@ class verilog:
else:
self.vf.write(" if (!csb{0}_reg) begin\n".format(port))
if self.write_size:
if self.write_size != self.word_size:
for mask in range(0, self.num_wmasks):
lower = mask * self.write_size
upper = lower + self.write_size - 1

View File

@ -24,7 +24,7 @@ class cacti(simulation):
# self.targ_read_ports = []
# self.targ_write_ports = []
# self.period = 0
# if self.write_size:
# if self.write_size != self.word_size:
# self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
# else:
# self.num_wmasks = 0
@ -51,7 +51,7 @@ class cacti(simulation):
debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0' * self.addr_size, 0)
self.set_probe('0' * self.bank_addr_size, 0)
self.create_graph()
self.set_internal_spice_names()
self.create_measurement_names()

View File

@ -43,7 +43,7 @@ class delay(simulation):
self.targ_read_ports = []
self.targ_write_ports = []
self.period = 0
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@ -342,7 +342,7 @@ class delay(simulation):
except ValueError:
debug.error("Probe Address is not of binary form: {0}".format(self.probe_address), 1)
if len(self.probe_address) != self.addr_size:
if len(self.probe_address) != self.bank_addr_size:
debug.error("Probe Address's number of bits does not correspond to given SRAM", 1)
if not isinstance(self.probe_data, int) or self.probe_data>self.word_size or self.probe_data<0:
@ -455,7 +455,7 @@ class delay(simulation):
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name, write_port, i),
v_val=0)
for port in self.all_ports:
for i in range(self.addr_size):
for i in range(self.bank_addr_size):
self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name, port, i),
v_val=0)
@ -1391,7 +1391,7 @@ class delay(simulation):
"""
for port in self.all_ports:
for i in range(self.addr_size):
for i in range(self.bank_addr_size):
sig_name = "{0}{1}_{2}".format(self.addr_name, port, i)
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05)

View File

@ -21,7 +21,7 @@ class elmore(simulation):
# self.targ_read_ports = []
# self.targ_write_ports = []
# self.period = 0
# if self.write_size:
# if self.write_size != self.word_size:
# self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
# else:
# self.num_wmasks = 0
@ -45,7 +45,7 @@ class elmore(simulation):
debug.warning("In analytical mode, all ports have the timing of the first read port.")
# Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0' * self.addr_size, 0)
self.set_probe('0' * self.bank_addr_size, 0)
self.create_graph()
self.set_internal_spice_names()
self.create_measurement_names()
@ -106,4 +106,4 @@ class elmore(simulation):
power.leakage /= 1e6
debug.info(1, "Dynamic Power: {0} mW".format(power.dynamic))
debug.info(1, "Leakage Power: {0} mW".format(power.leakage))
return power
return power

View File

@ -44,7 +44,7 @@ class functional(simulation):
else:
self.output_path = output_path
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@ -60,7 +60,7 @@ class functional(simulation):
self.addr_spare_index = -int(math.log(self.words_per_row) / math.log(2))
else:
# This will select the entire address when one word per row
self.addr_spare_index = self.addr_size
self.addr_spare_index = self.bank_addr_size
# If trim is set, specify the valid addresses
self.valid_addresses = set()
self.max_address = self.num_rows * self.words_per_row - 1
@ -68,7 +68,7 @@ class functional(simulation):
for i in range(self.words_per_row):
self.valid_addresses.add(i)
self.valid_addresses.add(self.max_address - i - 1)
self.probe_address, self.probe_data = '0' * self.addr_size, 0
self.probe_address, self.probe_data = '0' * self.bank_addr_size, 0
self.set_corner(corner)
self.set_spice_constants()
self.set_stimulus_variables()
@ -133,7 +133,7 @@ class functional(simulation):
def create_random_memory_sequence(self):
# Select randomly, but have 3x more reads to increase probability
if self.write_size:
if self.write_size != self.word_size:
rw_ops = ["noop", "write", "partial_write", "read", "read"]
w_ops = ["noop", "write", "partial_write"]
else:
@ -142,7 +142,7 @@ class functional(simulation):
r_ops = ["noop", "read"]
# First cycle idle is always an idle cycle
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.bank_addr_size, "0" * self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
@ -244,7 +244,7 @@ class functional(simulation):
self.t_current += self.period
# Last cycle idle needed to correctly measure the value on the second to last clock edge
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.addr_size, "0" * self.num_wmasks, 0, self.t_current)
comment = self.gen_cycle_comment("noop", "0" * self.word_size, "0" * self.bank_addr_size, "0" * self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
def gen_masked_data(self, old_word, word, wmask):
@ -366,7 +366,7 @@ class functional(simulation):
random_value = random.sample(self.valid_addresses, 1)[0]
else:
random_value = random.randint(0, self.max_address)
addr_bits = binary_repr(random_value, self.addr_size)
addr_bits = binary_repr(random_value, self.bank_addr_size)
return addr_bits
def get_data(self):
@ -426,7 +426,7 @@ class functional(simulation):
# Generate address bits
for port in self.all_ports:
for bit in range(self.addr_size):
for bit in range(self.bank_addr_size):
sig_name="{0}{1}_{2} ".format(self.addr_name, port, bit)
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05)
@ -440,7 +440,7 @@ class functional(simulation):
# Generate wmask bits
for port in self.write_ports:
if self.write_size:
if self.write_size != self.word_size:
self.sf.write("\n* Generation of wmask signals\n")
for bit in range(self.num_wmasks):
sig_name = "WMASK{0}_{1} ".format(port, bit)

View File

@ -183,7 +183,8 @@ class lib:
# set the read and write port as inputs.
self.write_data_bus(port)
self.write_addr_bus(port)
if self.sram.write_size and port in self.write_ports:
if self.sram.write_size != self.sram.word_size and \
port in self.write_ports:
self.write_wmask_bus(port)
# need to split this into sram and port control signals
self.write_control_pins(port)
@ -193,8 +194,8 @@ class lib:
def write_footer(self):
""" Write the footer """
self.lib.write(" }\n") #Closing brace for the cell
self.lib.write("}\n") #Closing brace for the library
self.lib.write(" }\n") # Closing brace for the cell
self.lib.write("}\n") # Closing brace for the library
def write_header(self):
""" Write the header information """
@ -378,7 +379,7 @@ class lib:
self.lib.write(" bit_to : 0;\n")
self.lib.write(" }\n\n")
if self.sram.write_size:
if self.sram.write_size != self.sram.word_size:
self.lib.write(" type (wmask){\n")
self.lib.write(" base_type : array;\n")
self.lib.write(" data_type : bit;\n")

View File

@ -20,7 +20,7 @@ class simulation():
self.name = self.sram.name
self.word_size = self.sram.word_size
self.addr_size = self.sram.addr_size
self.bank_addr_size = self.sram.bank_addr_size
self.write_size = self.sram.write_size
self.num_spare_rows = self.sram.num_spare_rows
if not self.sram.num_spare_cols:
@ -39,7 +39,7 @@ class simulation():
self.words_per_row = self.sram.words_per_row
self.num_rows = self.sram.num_rows
self.num_cols = self.sram.num_cols
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@ -80,7 +80,7 @@ class simulation():
self.dout_name = "dout"
self.pins = self.gen_pin_names(port_signal_names=(self.addr_name, self.din_name, self.dout_name),
port_info=(len(self.all_ports), self.write_ports, self.read_ports),
abits=self.addr_size,
abits=self.bank_addr_size,
dbits=self.word_size + self.num_spare_cols)
debug.check(len(self.sram.pins) == len(self.pins),
"Number of pins generated for characterization \
@ -103,7 +103,7 @@ class simulation():
self.spare_wen_value = {port: [] for port in self.write_ports}
# Three dimensional list to handle each addr and data bits for each port over the number of checks
self.addr_values = {port: [[] for bit in range(self.addr_size)] for port in self.all_ports}
self.addr_values = {port: [[] for bit in range(self.bank_addr_size)] for port in self.all_ports}
self.data_values = {port: [[] for bit in range(self.word_size + self.num_spare_cols)] for port in self.write_ports}
self.wmask_values = {port: [[] for bit in range(self.num_wmasks)] for port in self.write_ports}
self.spare_wen_values = {port: [[] for bit in range(self.num_spare_cols)] for port in self.write_ports}
@ -174,10 +174,10 @@ class simulation():
def add_address(self, address, port):
""" Add the array of address values """
debug.check(len(address)==self.addr_size, "Invalid address size.")
debug.check(len(address)==self.bank_addr_size, "Invalid address size.")
self.addr_value[port].append(address)
bit = self.addr_size - 1
bit = self.bank_addr_size - 1
for c in address:
if c=="0":
self.addr_values[port][bit].append(0)
@ -330,7 +330,7 @@ class simulation():
try:
self.add_address(self.addr_value[port][-1], port)
except:
self.add_address("0" * self.addr_size, port)
self.add_address("0" * self.bank_addr_size, port)
# If the port is also a readwrite then add
# the same value as previous cycle
@ -464,7 +464,7 @@ class simulation():
for port in range(total_ports):
pin_names.append("{0}{1}".format("clk", port))
if self.write_size:
if self.write_size != self.word_size:
for port in write_index:
for bit in range(self.num_wmasks):
pin_names.append("WMASK{0}_{1}".format(port, bit))

View File

@ -97,6 +97,10 @@ log.create_file = True
def info(lev, str):
from globals import OPTS
# 99 is a special never print level
if lev == 99:
return
if (OPTS.verbose_level >= lev):
frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0])

View File

@ -378,6 +378,11 @@ def read_config(config_file, is_unit_test=True):
ports,
OPTS.tech_name)
# If write size is not defined, set it equal to word size
if OPTS.write_size is None:
OPTS.write_size = OPTS.word_size
def end_openram():
""" Clean up openram for a proper exit """
cleanup_paths()

View File

@ -27,7 +27,7 @@ class bank(design):
self.sram_config = sram_config
sram_config.set_local_config(self)
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@ -44,10 +44,7 @@ class bank(design):
# The local control signals are gated when we have bank select logic,
# so this prefix will be added to all of the input signals to create
# the internal gated signals.
if self.num_banks>1:
self.prefix="gated_"
else:
self.prefix=""
self.prefix=""
self.create_netlist()
if not OPTS.netlist_only:
@ -93,14 +90,11 @@ class bank(design):
for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("din{0}_{1}".format(port, bit), "INPUT")
for port in self.all_ports:
for bit in range(self.addr_size):
for bit in range(self.bank_addr_size):
self.add_pin("addr{0}_{1}".format(port, bit), "INPUT")
# For more than one bank, we have a bank select and name
# the signals gated_*.
if self.num_banks > 1:
for port in self.all_ports:
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.all_ports:
@ -128,8 +122,6 @@ class bank(design):
self.route_port_address(port)
self.route_column_address_lines(port)
self.route_control_lines(port)
if self.num_banks > 1:
self.route_bank_select(port)
self.route_supplies()
@ -171,7 +163,6 @@ class bank(design):
self.create_port_data()
self.create_port_address()
self.create_column_decoder()
self.create_bank_select()
def compute_instance_offsets(self):
"""
@ -252,8 +243,6 @@ class bank(design):
y_offset = min(self.column_decoder_offsets[port].y, self.port_data[port].column_mux_offset.y)
else:
y_offset = self.port_address_offsets[port].y
if self.num_banks > 1:
y_offset += self.bank_select.height + drc("well_to_well")
self.bank_select_offsets[port] = vector(-x_offset, -y_offset)
def compute_instance_port1_offsets(self):
@ -311,22 +300,25 @@ class bank(design):
self.place_port_address(self.port_address_offsets)
self.place_column_decoder(self.column_decoder_offsets)
self.place_bank_select(self.bank_select_offsets)
# self.place_bank_select(self.bank_select_offsets)
def compute_sizes(self):
""" Computes the required sizes to create the bank """
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
self.num_cols = int(self.words_per_row * self.word_size)
self.num_rows_temp = int(self.num_words / self.words_per_row)
self.num_rows_temp = int(self.num_words_per_bank / self.words_per_row)
self.num_rows = self.num_rows_temp + self.num_spare_rows
self.row_addr_size = ceil(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.addr_size = self.col_addr_size + self.row_addr_size
self.bank_addr_size = self.col_addr_size + self.row_addr_size
debug.check(self.num_rows_temp * self.num_cols==self.word_size * self.num_words,
debug.check(self.num_rows_temp * self.num_cols * self.num_banks ==self.word_size * self.num_words,
"Invalid bank sizes.")
debug.check(self.addr_size==self.col_addr_size + self.row_addr_size,
debug.check(self.bank_addr_size==self.col_addr_size + self.row_addr_size,
"Invalid address break down.")
# The order of the control signals on the control bus:
@ -351,11 +343,7 @@ class bank(design):
# These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = []
for port in self.all_ports:
if self.num_banks > 1:
self.control_signals.append(["gated_" + str for str in self.input_control_signals[port]])
else:
self.control_signals.append(self.input_control_signals[port])
self.control_signals.append(self.input_control_signals[port])
# The central bus is the column address (one hot) and row address (binary)
if self.col_addr_size>0:
@ -366,8 +354,8 @@ class bank(design):
# Gap between decoder and array
self.decoder_gap = max(2 * drc("pwell_to_nwell") + drc("nwell_enclose_active"),
2 * self.m2_pitch,
drc("nwell_to_nwell"))
2 * self.m2_pitch,
drc("nwell_to_nwell"))
def add_modules(self):
""" Add all the modules using the class loader """
@ -405,9 +393,6 @@ class bank(design):
port=port,
bit_offsets=self.bit_offsets))
if(self.num_banks > 1):
self.bank_select = factory.create(module_type="bank_select")
def create_bitcell_array(self):
""" Creating Bitcell Array """
self.bitcell_array_inst=self.add_inst(name="bitcell_array",
@ -560,7 +545,7 @@ class bank(design):
def create_bank_select(self):
""" Create the bank select logic. """
if not self.num_banks > 1:
if not self.num_banks < 2:
return
self.bank_select_inst = [None] * len(self.all_ports)
@ -578,7 +563,7 @@ class bank(design):
def place_bank_select(self, offsets):
""" Place the bank select logic. """
if not self.num_banks > 1:
if not self.num_banks < 2:
return
debug.check(len(offsets)>=len(self.all_ports),
@ -603,7 +588,7 @@ class bank(design):
self.copy_layout_pin(inst, "gnd")
if 'vpb' in self.bitcell_array_inst.mod.pins and 'vnb' in self.bitcell_array_inst.mod.pins:
for pin_name, supply_name in zip(['vnb','vpb'],['gnd','vdd']):
for pin_name, supply_name in zip(['vnb', 'vpb'], ['gnd', 'vdd']):
self.copy_layout_pin(self.bitcell_array_inst, pin_name, new_name=supply_name)
# If we use the pinvbuf as the decoder, we need to add power pins.
@ -684,7 +669,7 @@ class bank(design):
names=self.control_signals[0],
length=control_bus_length,
vertical=True,
make_pins=(self.num_banks==1),
make_pins=(True),
pitch=self.m3_pitch)
self.copy_layout_pin(self.port_address_inst[0], "wl_en", self.prefix + "wl_en0")
@ -701,7 +686,7 @@ class bank(design):
names=list(reversed(self.control_signals[1])),
length=control_bus_length,
vertical=True,
make_pins=(self.num_banks==1),
make_pins=(True),
pitch=self.m3_pitch)
self.copy_layout_pin(self.port_address_inst[1], "wl_en", self.prefix + "wl_en1")
@ -763,7 +748,7 @@ class bank(design):
din_name = "din{0}_{1}".format(port, row)
self.copy_layout_pin(self.port_data_inst[port], data_name, din_name)
if self.write_size:
if self.write_size != self.word_size:
for row in range(self.num_wmasks):
wmask_name = "bank_wmask_{}".format(row)
bank_wmask_name = "bank_wmask{0}_{1}".format(port, row)

View File

@ -1,313 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from tech import drc
from base import design
from base import vector
from pgates import pinv
from pgates import pnand2
from pgates import pnor2
from sram_factory import factory
from globals import OPTS
class bank_select(design):
"""Create a bank select signal that is combined with an array of
NOR+INV gates to gate the control signals in case of multiple
banks are created in upper level SRAM module
"""
def __init__(self, name="bank_select", port="rw"):
super().__init__(name)
self.port = port
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.add_pins()
self.add_modules()
self.create_instances()
def create_layout(self):
self.calculate_module_offsets()
self.place_instances()
self.route_instances()
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
self.width = max([x.rx() for x in self.inv_inst])
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
# Number of control lines in the bus
if self.port == "rw":
self.num_control_lines = 4
else:
self.num_control_lines = 3
# The order of the control signals on the control bus:
# FIXME: Update for multiport (these names are not right)
self.input_control_signals = ["clk_buf", "clk_buf_bar"]
if (self.port == "rw") or (self.port == "w"):
self.input_control_signals.append("w_en")
if (self.port == "rw") or (self.port == "r"):
self.input_control_signals.append("s_en")
# These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_" + str for str in self.input_control_signals]
self.add_pin_list(self.input_control_signals, "INPUT")
self.add_pin("bank_sel")
self.add_pin_list(self.control_signals, "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
""" Create modules for later instantiation """
self.dff = factory.create(module_type="dff")
height = self.dff.height + drc("poly_to_active")
# 1x Inverter
self.inv_sel = factory.create(module_type="pinv", height=height)
# 4x Inverter
self.inv4x = factory.create(module_type="pinv", height=height, size=4)
self.nor2 = factory.create(module_type="pnor2", height=height)
self.inv4x_nor = factory.create(module_type="pinv", height=height, size=4)
self.nand2 = factory.create(module_type="pnand2", height=height)
def calculate_module_offsets(self):
self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_bank_sel_inv = 0
self.xoffset_inputs = 0
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
def create_instances(self):
self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
mod=self.inv_sel)
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
self.logic_inst = []
self.inv_inst = []
for i in range(self.num_control_lines):
input_name = self.input_control_signals[i]
gated_name = self.control_signals[i]
name_nand = "nand_{}".format(input_name)
name_nor = "nor_{}".format(input_name)
name_inv = "inv_{}".format(input_name)
# These require OR (nor2+inv) gates since they are active low.
# (writes occur on clk low)
if input_name in ("clk_buf"):
self.logic_inst.append(self.add_inst(name=name_nor,
mod=self.nor2))
self.connect_inst([input_name,
"bank_sel_bar",
gated_name + "_temp_bar",
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x_nor))
self.connect_inst([gated_name + "_temp_bar",
gated_name,
"vdd",
"gnd"])
# the rest are AND (nand2+inv) gates
else:
self.logic_inst.append(self.add_inst(name=name_nand,
mod=self.nand2))
self.connect_inst([input_name,
"bank_sel",
gated_name + "_temp_bar",
"vdd",
"gnd"])
# They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x))
self.connect_inst([gated_name + "_temp_bar",
gated_name,
"vdd",
"gnd"])
def place_instances(self):
# bank select inverter
self.bank_select_inv_position = vector(self.xoffset_bank_sel_inv, 0)
# bank select inverter (must be made unique if more than one OR)
self.bank_sel_inv.place(vector(self.xoffset_bank_sel_inv, 0))
for i in range(self.num_control_lines):
logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i]
if i == 0:
y_offset = 0
else:
y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
if i % 2:
y_offset += self.inv4x.height
mirror = "MX"
else:
mirror = ""
# These require OR (nor2+inv) gates since they are active low.
# (writes occur on clk low)
if input_name in ("clk_buf"):
logic_inst.place(offset=[self.xoffset_nor, y_offset],
mirror=mirror)
# the rest are AND (nand2+inv) gates
else:
logic_inst.place(offset=[self.xoffset_nand, y_offset],
mirror=mirror)
# They all get inverters on the output
inv_inst.place(offset=[logic_inst.rx(), y_offset],
mirror=mirror)
def route_instances(self):
# bank_sel is vertical wire
bank_sel_inv_pin = self.bank_sel_inv.get_pin("A")
xoffset_bank_sel = bank_sel_inv_pin.lx()
bank_sel_line_pos = vector(xoffset_bank_sel, 0)
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end])
self.add_via_center(layers=self.m1_stack,
offset=bank_sel_inv_pin.center())
# Route the pin to the left edge as well
bank_sel_pin_pos=vector(0, 0)
bank_sel_pin_end=vector(bank_sel_line_pos.x, bank_sel_pin_pos.y)
self.add_layout_pin_segment_center(text="bank_sel",
layer="m3",
start=bank_sel_pin_pos,
end=bank_sel_pin_end)
self.add_via_center(layers=self.m2_stack,
offset=bank_sel_pin_end,
directions=("H", "H"))
# bank_sel_bar is vertical wire
bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z")
xoffset_bank_sel_bar = bank_sel_bar_pin.rx()
self.add_label_pin(text="bank_sel_bar",
layer="m2",
offset=vector(xoffset_bank_sel_bar, 0),
height=self.inv4x.height)
self.add_via_center(layers=self.m1_stack,
offset=bank_sel_bar_pin.rc())
for i in range(self.num_control_lines):
logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i]
gated_name = self.control_signals[i]
if input_name in ("clk_buf"):
xoffset_bank_signal = xoffset_bank_sel_bar
else:
xoffset_bank_signal = xoffset_bank_sel
# Connect the logic output to inverter input
out_pin = logic_inst.get_pin("Z")
out_pos = out_pin.center()
in_pin = inv_inst.get_pin("A")
in_pos = in_pin.center()
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
# Connect the logic B input to bank_sel / bank_sel_bar
logic_pin = logic_inst.get_pin("B")
logic_pos = logic_pin.center()
input_pos = vector(xoffset_bank_signal, logic_pos.y)
self.add_path("m3", [logic_pos, input_pos])
self.add_via_center(self.m2_stack,
input_pos)
self.add_via_stack_center(from_layer=logic_pin.layer,
to_layer="m3",
offset=logic_pos)
# Connect the logic A input to the input pin
logic_pin = logic_inst.get_pin("A")
logic_pos = logic_pin.center()
input_pos = vector(0, logic_pos.y)
self.add_via_stack_center(from_layer=logic_pin.layer,
to_layer="m3",
offset=logic_pos)
self.add_layout_pin_segment_center(text=input_name,
layer="m3",
start=input_pos,
end=logic_pos)
# Add output pins
out_pin = inv_inst.get_pin("Z")
self.add_layout_pin(text=gated_name,
layer=out_pin.layer,
offset=out_pin.ll(),
width=inv_inst.rx() - out_pin.lx(),
height=out_pin.height())
# Find the x offsets for where the vias/pins should be placed
a_xoffset = self.logic_inst[0].lx()
b_xoffset = self.inv_inst[0].lx()
for num in range(self.num_control_lines):
# Route both supplies
for n in ["vdd", "gnd"]:
supply_pin = self.inv_inst[num].get_pin(n)
supply_offset = supply_pin.ll().scale(0, 1)
self.add_rect(layer="m1",
offset=supply_offset,
width=self.width)
# Add pins in two locations
for xoffset in [a_xoffset, b_xoffset]:
pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=self.m1_stack,
offset=pin_pos,
directions=("H", "H"))
self.add_via_center(layers=self.m2_stack,
offset=pin_pos,
directions=("H", "H"))
self.add_layout_pin_rect_center(text=n,
layer="m3",
offset=pin_pos)
# Add vdd/gnd supply rails
gnd_pin = self.inv_inst[num].get_pin("gnd")
left_gnd_pos = vector(0, gnd_pin.cy())
self.add_layout_pin_segment_center(text="gnd",
layer="m1",
start=left_gnd_pos,
end=gnd_pin.rc())
vdd_pin = self.inv_inst[num].get_pin("vdd")
left_vdd_pos = vector(0, vdd_pin.cy())
self.add_layout_pin_segment_center(text="vdd",
layer="m1",
start=left_vdd_pos,
end=vdd_pin.rc())

View File

@ -5,74 +5,22 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from base import design
import debug
from sram_factory import factory
import math
from base import vector
from globals import OPTS
from base import logical_effort
from .control_logic_base import control_logic_base
class control_logic(design):
class control_logic(control_logic_base):
"""
Dynamically generated Control logic for the total SRAM circuit.
"""
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
""" Constructor """
name = "control_logic_" + port_type
super().__init__(name)
debug.info(1, "Creating {}".format(name))
self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row))
self.add_comment("word_size {0}".format(word_size))
self.sram=sram
self.num_rows = num_rows
self.words_per_row = words_per_row
self.word_size = word_size
self.port_type = port_type
if not spare_columns:
self.num_spare_cols = 0
else:
self.num_spare_cols = spare_columns
self.num_cols = word_size * words_per_row + self.num_spare_cols
self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model.
# FIXME: This should be made a parameter
self.wl_timing_tolerance = 1
self.wl_stage_efforts = None
self.sen_stage_efforts = None
if self.port_type == "rw":
self.num_control_signals = 2
else:
self.num_control_signals = 1
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.setup_signal_busses()
self.add_pins()
self.add_modules()
self.create_instances()
def create_layout(self):
""" Create layout and route between modules """
self.place_instances()
self.route_all()
# self.add_lvs_correspondence_points()
self.add_boundary()
self.DRC_LVS()
super().__init__(num_rows, words_per_row, word_size, spare_columns, sram, port_type, name)
def add_pins(self):
""" Add the pins to the control logic module. """
@ -151,93 +99,6 @@ class control_logic(design):
self.delay_chain=factory.create(module_type="delay_chain",
fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
from math import ceil
previous_delay_chain_delay = (previous_fanout + 1 + self.inv_parasitic_delay) * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
# This can be anything >=2
delay_fanout = 3
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay = self.wl_delay * self.wl_timing_tolerance - (self.sen_delay - previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
# force an even number of stages.
if delay_stages % 2 == 1:
delay_stages += 1
# Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout)
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay_fall = self.wl_delay_fall * self.wl_timing_tolerance - \
(self.sen_delay_fall - previous_delay_chain_delay / 2)
required_delay_rise = self.wl_delay_rise * self.wl_timing_tolerance - \
(self.sen_delay_rise - previous_delay_chain_delay / 2)
debug.info(2,
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
required_delay_rise))
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5
stages_close = False
# The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,
fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,
fanout_rise)
debug.info(1,
"Fall stages={}, rise stages={}".format(stages_fall,
stages_rise))
if abs(stages_fall - stages_rise) == 1 and not stages_close:
stages_close = True
safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall
if stages_fall == stages_rise:
break
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.")
fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall
break
# There should also be a condition to make sure the fanout does not get too large.
# Otherwise, increase the fanout of delay with the most stages, calculate new stages
elif stages_fall>stages_rise:
fanout_fall+=1
else:
fanout_rise+=1
total_stages = max(stages_fall, stages_rise) * 2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
# 3 is the minimum delay per stage (with pinv=0).
if required_delay <= 3 + self.inv_parasitic_delay:
return 1
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages
def setup_signal_busses(self):
""" Setup bus names, determine the size of the busses etc """
@ -277,17 +138,6 @@ class control_logic(design):
self.supply_list = ["vdd", "gnd"]
def route_rails(self):
""" Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch
# DFF spacing plus the power routing
offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0)
self.input_bus = self.create_vertical_bus("m2",
offset,
self.internal_bus_list,
height)
def create_instances(self):
""" Create all the instances """
self.create_dffs()
@ -303,21 +153,8 @@ class control_logic(design):
self.create_delay()
self.create_pen_row()
def place_instances(self):
""" Place all the instances """
# Keep track of all right-most instances to determine row boundary
# and add the vdd/gnd pins
self.row_end_inst = []
# 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
# as well as the power supply stripe
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch
def place_logic_rows(self):
row = 0
# Add the logic on the right of the bus
self.place_clk_buf_row(row)
row += 1
self.place_gated_clk_bar_row(row)
@ -336,24 +173,8 @@ class control_logic(design):
self.place_rbl_delay_row(row)
row += 1
self.place_wlen_row(row)
row += 1
control_center_y = self.wl_en_inst.uy() + self.m3_pitch
# Delay chain always gets placed at row 4
self.place_delay(4)
height = self.delay_inst.uy()
# This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
# 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.delay_inst.rx(), self.width)
self.width += self.m2_pitch
self.control_center_y = self.wl_en_inst.uy() + self.m3_pitch
def route_all(self):
""" Routing between modules """
@ -373,24 +194,12 @@ class control_logic(design):
self.route_supplies()
def create_delay(self):
""" Create the replica bitline """
""" Create the delay chain """
self.delay_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain)
# rbl_bl_delay is asserted (1) when the bitline has been discharged
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
def place_delay(self, row):
""" Place the replica bitline """
debug.check(row % 2 == 0, "Must place delay chain at even row for supply alignment.")
# It is flipped on X axis
y_off = row * self.and2.height + self.delay_chain.height
# Add the RBL above the rows
# Add to the right of the control rows and routing channel
offset = vector(0, y_off)
self.delay_inst.place(offset, mirror="MX")
def route_delay(self):
out_pos = self.delay_inst.get_pin("out").center()
@ -406,109 +215,6 @@ class control_logic(design):
# Input from RBL goes to the delay line for futher delay
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf",
mod=self.clk_buf_driver)
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def place_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.clk_buf_inst)
def route_clk_buf(self):
clk_pin = self.clk_buf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_rect_center(text="clk",
layer="m2",
offset=clk_pos)
self.add_via_stack_center(from_layer=clk_pin.layer,
to_layer="m2",
offset=clk_pos)
self.route_output_to_bus_jogged(self.clk_buf_inst,
"clk_buf")
self.connect_output(self.clk_buf_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(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
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.input_bus)
out_pin = self.clk_bar_inst.get_pin("Z")
out_pos = out_pin.center()
in_pin = self.gated_clk_bar_inst.get_pin("A")
in_pos = in_pin.center()
self.add_zjog(out_pin.layer, out_pos, in_pos)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer=in_pin.layer,
offset=in_pos)
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["B"], ["cs"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
b_pin = self.gated_clk_bar_inst.get_pin("B")
self.add_via_stack_center(from_layer=b_pin.layer,
to_layer="m3",
offset=b_pin.center())
# This is the second gate over, so it needs to be on M3
self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
"gated_clk_bar")
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):
x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
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.input_bus)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
z_pin = self.gated_clk_buf_inst.get_pin("Z")
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2",
offset=z_pin.center())
def create_wlen_row(self):
# input pre_p_en, output: wl_en
self.wl_en_inst=self.add_inst(name="buf_wl_en",
@ -651,194 +357,3 @@ class control_logic(design):
self.connect_vertical_bus(wen_map, self.w_en_gate_inst, self.input_bus)
self.connect_output(self.w_en_gate_inst, "Z", "w_en")
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
inst_pins = self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list
self.connect_inst(inst_pins)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0, 0))
def route_dffs(self):
if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r":
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1])
# Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = vector(in_pos.x, self.gated_clk_buf_inst.get_pin("vdd").cy() - self.m1_pitch)
rail_pos = vector(self.input_bus["clk_buf"].cx(), mid_pos.y)
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self, row):
""" Compute the y-offset and mirroring """
y_off = row * self.and2.height
if row % 2:
y_off += self.and2.height
mirror="MX"
else:
mirror="R0"
return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name)
out_pos = out_pin.center()
right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
self.add_layout_pin_segment_center(text=out_name,
layer="m2",
start=out_pos,
end=right_pos)
def route_supplies(self):
""" Add vdd and gnd to the instance cells """
pin_layer = self.dff.get_pin("vdd").layer
supply_layer = self.supply_stack[2]
# FIXME: We should be able to replace this with route_vertical_pins instead
# but we may have to make the logic gates a separate module so that they
# have row pins of the same width
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
min_row_x_loc = self.control_x_offset
vdd_pin_locs = []
gnd_pin_locs = []
last_via = None
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
vdd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
pins = inst.get_pins("gnd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(min_row_x_loc, pin.rc().y)
gnd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
if last_via:
via_height=last_via.mod.second_layer_height
via_width=last_via.mod.second_layer_width
else:
via_height=None
via_width=0
min_y = min([x.y for x in vdd_pin_locs])
max_y = max([x.y for x in vdd_pin_locs])
bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="vdd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
min_y = min([x.y for x in gnd_pin_locs])
max_y = max([x.y for x in gnd_pin_locs])
bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="gnd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
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.
"""
# pin=self.clk_inv1.get_pin("Z")
# self.add_label_pin(text="clk1_bar",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
# pin=self.clk_inv2.get_pin("Z")
# self.add_label_pin(text="clk2",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
pin=self.delay_inst.get_pin("out")
self.add_label_pin(text="out",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)
if self.port_type=="rw" or self.port_type=="w":
self.graph_inst_exclude.add(self.w_en_gate_inst)
def place_util(self, inst, x_offset, row):
""" Utility to place a row and compute the next offset """
(y_offset, mirror) = self.get_offset(row)
offset = vector(x_offset, y_offset)
inst.place(offset, mirror)
return x_offset + inst.width
def route_output_to_bus_jogged(self, inst, name):
# Connect this at the bottom of the buffer
out_pin = inst.get_pin("Z")
out_pos = out_pin.center()
mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height)
mid2 = vector(self.input_bus[name].cx(), mid1.y)
bus_pos = self.input_bus[name].center()
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
def get_left_pins(self, name):
"""
Return the left side supply pins to connect to a vertical stripe.
"""
return(self.cntrl_dff_inst.get_pins(name) + self.delay_inst.get_pins(name))

View File

@ -0,0 +1,508 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from base import design
import debug
from sram_factory import factory
import math
from base import vector
from globals import OPTS
from base import logical_effort
class control_logic_base(design):
"""
Generic base class for SRAM control logic.
"""
def __init__(self, num_rows, words_per_row, word_size, spare_columns=None, sram=None, port_type="rw", name=""):
""" Constructor """
name = "control_logic_" + port_type
super().__init__(name)
debug.info(1, "Creating {}".format(name))
self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row))
self.add_comment("word_size {0}".format(word_size))
self.sram=sram
self.num_rows = num_rows
self.words_per_row = words_per_row
self.word_size = word_size
self.port_type = port_type
if not spare_columns:
self.num_spare_cols = 0
else:
self.num_spare_cols = spare_columns
self.num_cols = word_size * words_per_row + self.num_spare_cols
self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model.
# FIXME: This should be made a parameter
self.wl_timing_tolerance = 1
self.wl_stage_efforts = None
self.sen_stage_efforts = None
if self.port_type == "rw":
self.num_control_signals = 2
else:
self.num_control_signals = 1
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
def create_netlist(self):
self.setup_signal_busses()
self.add_pins()
self.add_modules()
self.create_instances()
def create_layout(self):
""" Create layout and route between modules """
self.place_instances()
self.route_all()
# self.add_lvs_correspondence_points()
self.add_boundary()
self.DRC_LVS()
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
from math import ceil
previous_delay_chain_delay = (previous_fanout + 1 + self.inv_parasitic_delay) * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
# This can be anything >=2
delay_fanout = 3
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay = self.wl_delay * self.wl_timing_tolerance - (self.sen_delay - previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
# force an even number of stages.
if delay_stages % 2 == 1:
delay_stages += 1
# Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout)
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay_fall = self.wl_delay_fall * self.wl_timing_tolerance - \
(self.sen_delay_fall - previous_delay_chain_delay / 2)
required_delay_rise = self.wl_delay_rise * self.wl_timing_tolerance - \
(self.sen_delay_rise - previous_delay_chain_delay / 2)
debug.info(2,
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
required_delay_rise))
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5
stages_close = False
# The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,
fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,
fanout_rise)
debug.info(1,
"Fall stages={}, rise stages={}".format(stages_fall,
stages_rise))
if abs(stages_fall - stages_rise) == 1 and not stages_close:
stages_close = True
safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall
if stages_fall == stages_rise:
break
elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.")
fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall
break
# There should also be a condition to make sure the fanout does not get too large.
# Otherwise, increase the fanout of delay with the most stages, calculate new stages
elif stages_fall>stages_rise:
fanout_fall+=1
else:
fanout_rise+=1
total_stages = max(stages_fall, stages_rise) * 2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
# 3 is the minimum delay per stage (with pinv=0).
if required_delay <= 3 + self.inv_parasitic_delay:
return 1
delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages
def route_rails(self):
""" Add the input signal inverted tracks """
height = self.control_logic_center.y - self.m2_pitch
# DFF spacing plus the power routing
offset = vector(self.ctrl_dff_array.width + self.m4_pitch, 0)
self.input_bus = self.create_vertical_bus("m2",
offset,
self.internal_bus_list,
height)
def place_instances(self):
""" Place all the instances """
# Keep track of all right-most instances to determine row boundary
# and add the vdd/gnd pins
self.row_end_inst = []
# 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
# as well as the power supply stripe
self.control_x_offset = self.ctrl_dff_array.width + self.internal_bus_width + self.m4_pitch
self.place_logic_rows()
# Delay chain always gets placed at row 4
self.place_delay(4)
height = self.delay_inst.uy()
# This offset is used for placement of the control logic in the SRAM level.
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), self.control_center_y)
# 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.delay_inst.rx(), self.width)
self.width += self.m2_pitch
def place_delay(self, row):
""" Place the delay chain """
debug.check(row % 2 == 0, "Must place delay chain at even row for supply alignment.")
# It is flipped on X axis
y_off = row * self.and2.height + self.delay_chain.height
# Add to the right of the control rows and routing channel
offset = vector(0, y_off)
self.delay_inst.place(offset, mirror="MX")
def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf",
mod=self.clk_buf_driver)
self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def place_clk_buf_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
self.row_end_inst.append(self.clk_buf_inst)
def route_clk_buf(self):
clk_pin = self.clk_buf_inst.get_pin("A")
clk_pos = clk_pin.center()
self.add_layout_pin_rect_center(text="clk",
layer="m2",
offset=clk_pos)
self.add_via_stack_center(from_layer=clk_pin.layer,
to_layer="m2",
offset=clk_pos)
self.route_output_to_bus_jogged(self.clk_buf_inst,
"clk_buf")
self.connect_output(self.clk_buf_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(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
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.input_bus)
out_pin = self.clk_bar_inst.get_pin("Z")
out_pos = out_pin.center()
in_pin = self.gated_clk_bar_inst.get_pin("A")
in_pos = in_pin.center()
self.add_zjog(out_pin.layer, out_pos, in_pos)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer=in_pin.layer,
offset=in_pos)
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["B"], ["cs"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
b_pin = self.gated_clk_bar_inst.get_pin("B")
self.add_via_stack_center(from_layer=b_pin.layer,
to_layer="m3",
offset=b_pin.center())
# This is the second gate over, so it needs to be on M3
self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
"gated_clk_bar")
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):
x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
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.input_bus)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.input_bus,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
z_pin = self.gated_clk_buf_inst.get_pin("Z")
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2",
offset=z_pin.center())
def create_dffs(self):
self.ctrl_dff_inst=self.add_inst(name="ctrl_dffs",
mod=self.ctrl_dff_array)
inst_pins = self.input_list + self.dff_output_list + ["clk_buf"] + self.supply_list
self.connect_inst(inst_pins)
def place_dffs(self):
self.ctrl_dff_inst.place(vector(0, 0))
def route_dffs(self):
if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r":
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.input_bus, self.m2_stack[::-1])
# Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = vector(in_pos.x, self.gated_clk_buf_inst.get_pin("vdd").cy() - self.m1_pitch)
rail_pos = vector(self.input_bus["clk_buf"].cx(), mid_pos.y)
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
self.copy_layout_pin(self.ctrl_dff_inst, "din_0", "csb")
if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self, row):
""" Compute the y-offset and mirroring """
y_off = row * self.and2.height
if row % 2:
y_off += self.and2.height
mirror="MX"
else:
mirror="R0"
return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name)
out_pos = out_pin.center()
right_pos = out_pos + vector(self.width - out_pin.cx(), 0)
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
self.add_layout_pin_segment_center(text=out_name,
layer="m2",
start=out_pos,
end=right_pos)
def route_supplies(self):
""" Add vdd and gnd to the instance cells """
pin_layer = self.dff.get_pin("vdd").layer
supply_layer = self.supply_stack[2]
# FIXME: We should be able to replace this with route_vertical_pins instead
# but we may have to make the logic gates a separate module so that they
# have row pins of the same width
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
min_row_x_loc = self.control_x_offset
vdd_pin_locs = []
gnd_pin_locs = []
last_via = None
for inst in self.row_end_inst:
pins = inst.get_pins("vdd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(max_row_x_loc, pin.rc().y)
vdd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
pins = inst.get_pins("gnd")
for pin in pins:
if pin.layer == pin_layer:
row_loc = pin.rc()
pin_loc = vector(min_row_x_loc, pin.rc().y)
gnd_pin_locs.append(pin_loc)
last_via = self.add_via_stack_center(from_layer=pin_layer,
to_layer=supply_layer,
offset=pin_loc,
min_area=True)
self.add_path(pin_layer, [row_loc, pin_loc])
if last_via:
via_height=last_via.mod.second_layer_height
via_width=last_via.mod.second_layer_width
else:
via_height=None
via_width=0
min_y = min([x.y for x in vdd_pin_locs])
max_y = max([x.y for x in vdd_pin_locs])
bot_pos = vector(max_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(max_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="vdd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
min_y = min([x.y for x in gnd_pin_locs])
max_y = max([x.y for x in gnd_pin_locs])
bot_pos = vector(min_row_x_loc, min_y - 0.5 * via_height)
top_pos = vector(min_row_x_loc, max_y + 0.5 * via_height)
self.add_layout_pin_segment_center(text="gnd",
layer=supply_layer,
start=bot_pos,
end=top_pos,
width=via_width)
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
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.
"""
# pin=self.clk_inv1.get_pin("Z")
# self.add_label_pin(text="clk1_bar",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
# pin=self.clk_inv2.get_pin("Z")
# self.add_label_pin(text="clk2",
# layer="m1",
# offset=pin.ll(),
# height=pin.height(),
# width=pin.width())
pin=self.delay_inst.get_pin("out")
self.add_label_pin(text="out",
layer=pin.layer,
offset=pin.ll(),
height=pin.height(),
width=pin.width())
def graph_exclude_dffs(self):
"""Exclude dffs from graph as they do not represent critical path"""
self.graph_inst_exclude.add(self.ctrl_dff_inst)
if self.port_type=="rw" or self.port_type=="w":
self.graph_inst_exclude.add(self.w_en_gate_inst)
def place_util(self, inst, x_offset, row):
""" Utility to place a row and compute the next offset """
(y_offset, mirror) = self.get_offset(row)
offset = vector(x_offset, y_offset)
inst.place(offset, mirror)
return x_offset + inst.width
def route_output_to_bus_jogged(self, inst, name):
# Connect this at the bottom of the buffer
out_pin = inst.get_pin("Z")
out_pos = out_pin.center()
mid1 = vector(out_pos.x, out_pos.y - 0.3 * inst.mod.height)
mid2 = vector(self.input_bus[name].cx(), mid1.y)
bus_pos = self.input_bus[name].center()
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer="m2",
offset=out_pos)
def get_left_pins(self, name):
"""
Return the left side supply pins to connect to a vertical stripe.
"""
return(self.cntrl_dff_inst.get_pins(name) + self.delay_inst.get_pins(name))

View File

@ -25,7 +25,7 @@ class port_data(design):
sram_config.set_local_config(self)
self.port = port
if self.write_size is not None:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
@ -93,7 +93,7 @@ class port_data(design):
if self.write_driver_array:
self.create_write_driver_array()
if self.write_size is not None:
if self.write_size != self.word_size:
self.create_write_mask_and_array()
else:
self.write_mask_and_array_inst = None
@ -245,7 +245,7 @@ class port_data(design):
offsets=self.bit_offsets,
write_size=self.write_size,
num_spare_cols=self.num_spare_cols)
if self.write_size is not None:
if self.write_size != self.word_size:
# RBLs don't get a write mask
self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
columns=self.num_cols,
@ -391,13 +391,13 @@ class port_data(design):
temp.append("sparebl_{0}".format(bit))
temp.append("sparebr_{0}".format(bit))
if self.write_size is not None:
if self.write_size != self.word_size:
for i in range(self.num_wmasks):
temp.append("wdriver_sel_{}".format(i))
for i in range(self.num_spare_cols):
temp.append("bank_spare_wen{}".format(i))
elif self.num_spare_cols and not self.write_size:
elif self.num_spare_cols and self.write_size == self.word_size:
temp.append("w_en")
for i in range(self.num_spare_cols):
temp.append("bank_spare_wen{}".format(i))

View File

@ -36,14 +36,10 @@ class sram():
self.name = name
if self.num_banks == 1:
from .sram_1bank import sram_1bank as sram
elif self.num_banks == 2:
from .sram_2bank import sram_2bank as sram
else:
debug.error("Invalid number of banks.", -1)
from .sram_1bank import sram_1bank as sram
self.s = sram(name, sram_config)
self.s.create_netlist()
if not OPTS.netlist_only:
self.s.create_layout()
@ -62,6 +58,10 @@ class sram():
def verilog_write(self, name):
self.s.verilog_write(name)
if self.num_banks != 1:
from .sram_multibank import sram_multibank
mb = sram_multibank(self.s)
mb.verilog_write(name[:-2] + '_top.v')
def extended_config_write(self, name):
"""Dump config file with all options.
@ -166,7 +166,7 @@ class sram():
# Write a verilog model
start_time = datetime.datetime.now()
vname = OPTS.output_path + self.s.name + ".v"
vname = OPTS.output_path + self.s.name + '.v'
debug.print_raw("Verilog: Writing to {0}".format(vname))
self.verilog_write(vname)
print_time("Verilog", datetime.datetime.now(), start_time)

View File

@ -6,18 +6,756 @@
# All rights reserved.
#
from base import vector
from .sram_base import sram_base
from base import channel_route
from router import router_tech
from globals import OPTS
from globals import OPTS, print_time
import datetime
import debug
from math import ceil
from importlib import reload
from base import design
from base import verilog
from base import lef
from sram_factory import factory
from tech import spice
class sram_1bank(sram_base):
class sram_1bank(design, verilog, lef):
"""
Procedures specific to a one bank SRAM.
"""
def __init__(self, name, sram_config):
sram_base.__init__(self, name, sram_config)
design.__init__(self, name)
lef.__init__(self, ["m1", "m2", "m3", "m4"])
verilog.__init__(self)
self.sram_config = sram_config
sram_config.set_local_config(self)
self.bank_insts = []
if self.write_size != self.word_size:
self.num_wmasks = int(ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
if not self.num_spare_cols:
self.num_spare_cols = 0
try:
from tech import power_grid
self.supply_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
# Route a M3/M4 grid
self.supply_stack = self.m3_stack
def add_pins(self):
""" Add pins for entire SRAM. """
for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("din{0}[{1}]".format(port, bit), "INPUT")
for port in self.all_ports:
for bit in range(self.bank_addr_size):
self.add_pin("addr{0}[{1}]".format(port, bit), "INPUT")
# These are used to create the physical pins
self.control_logic_inputs = []
self.control_logic_outputs = []
for port in self.all_ports:
if port in self.readwrite_ports:
self.control_logic_inputs.append(self.control_logic_rw.get_inputs())
self.control_logic_outputs.append(self.control_logic_rw.get_outputs())
elif port in self.write_ports:
self.control_logic_inputs.append(self.control_logic_w.get_inputs())
self.control_logic_outputs.append(self.control_logic_w.get_outputs())
else:
self.control_logic_inputs.append(self.control_logic_r.get_inputs())
self.control_logic_outputs.append(self.control_logic_r.get_outputs())
for port in self.all_ports:
self.add_pin("csb{}".format(port), "INPUT")
for port in self.readwrite_ports:
self.add_pin("web{}".format(port), "INPUT")
for port in self.all_ports:
self.add_pin("clk{}".format(port), "INPUT")
# add the optional write mask pins
for port in self.write_ports:
for bit in range(self.num_wmasks):
self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT")
if self.num_spare_cols == 1:
self.add_pin("spare_wen{0}".format(port), "INPUT")
else:
for bit in range(self.num_spare_cols):
self.add_pin("spare_wen{0}[{1}]".format(port, bit), "INPUT")
for port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
# Standard supply and ground names
try:
self.vdd_name = spice["power"]
except KeyError:
self.vdd_name = "vdd"
try:
self.gnd_name = spice["ground"]
except KeyError:
self.gnd_name = "gnd"
self.add_pin(self.vdd_name, "POWER")
self.add_pin(self.gnd_name, "GROUND")
self.ext_supplies = [self.vdd_name, self.gnd_name]
self.ext_supply = {"vdd" : self.vdd_name, "gnd" : self.gnd_name}
def add_global_pex_labels(self):
"""
Add pex labels at the sram level for spice analysis
"""
# add pex labels for bitcells
for bank_num in range(len(self.bank_insts)):
bank = self.bank_insts[bank_num]
pex_data = bank.reverse_transformation_bitcell(self.bitcell.name)
bank_offset = pex_data[0] # offset bank relative to sram
Q_offset = pex_data[1] # offset of storage relative to bank
Q_bar_offset = pex_data[2] # offset of storage relative to bank
bl_offsets = pex_data[3]
br_offsets = pex_data[4]
bl_meta = pex_data[5]
br_meta = pex_data[6]
bl = []
br = []
storage_layer_name = "m1"
bitline_layer_name = self.bitcell.get_pin("bl").layer
for cell in range(len(bank_offset)):
Q = [bank_offset[cell][0] + Q_offset[cell][0],
bank_offset[cell][1] + Q_offset[cell][1]]
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0],
bank_offset[cell][1] + Q_bar_offset[cell][1]]
OPTS.words_per_row = self.words_per_row
row = int(cell % (OPTS.num_words / self.words_per_row))
col = int(cell / (OPTS.num_words))
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q)
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q_bar)
for cell in range(len(bl_offsets)):
col = bl_meta[cell][0][2]
for bitline in range(len(bl_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
bl.append([bitline_location, bl_meta[cell][bitline][3], col])
for cell in range(len(br_offsets)):
col = br_meta[cell][0][2]
for bitline in range(len(br_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
br.append([bitline_location, br_meta[cell][bitline][3], col])
for i in range(len(bl)):
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]),
bitline_layer_name, bl[i][0])
for i in range(len(br)):
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]),
bitline_layer_name, br[i][0])
# add pex labels for control logic
for i in range(len(self.control_logic_insts)):
instance = self.control_logic_insts[i]
control_logic_offset = instance.offset
for output in instance.mod.output_list:
pin = instance.mod.get_pin(output)
pin.transform([0, 0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0],
control_logic_offset[1] + pin.center()[1]]
self.add_layout_pin_rect_center("{0}{1}".format(pin.name, i),
storage_layer_name,
offset)
def create_netlist(self):
""" Netlist creation """
start_time = datetime.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.datetime.now(), start_time)
def create_layout(self):
""" Layout creation """
start_time = datetime.datetime.now()
self.place_instances()
if not OPTS.is_unit_test:
print_time("Placement", datetime.datetime.now(), start_time)
start_time = datetime.datetime.now()
self.route_layout()
if not OPTS.is_unit_test:
print_time("Routing", datetime.datetime.now(), start_time)
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
highest_coord = self.find_highest_coords()
self.width = highest_coord[0]
self.height = highest_coord[1]
if OPTS.use_pex and OPTS.pex_exe[0] != "calibre":
debug.info(2, "adding global pex labels")
self.add_global_pex_labels()
self.add_boundary(ll=vector(0, 0),
ur=vector(self.width, self.height))
start_time = datetime.datetime.now()
if not OPTS.is_unit_test:
# We only enable final verification if we have routed the design
# Only run this if not a unit test, because unit test will also verify it.
self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc)
print_time("Verification", datetime.datetime.now(), start_time)
def create_modules(self):
debug.error("Must override pure virtual function.", -1)
def route_supplies(self, bbox=None):
""" Route the supply grid and connect the pins to them. """
# Copy the pins to the top level
# This will either be used to route or left unconnected.
for pin_name in ["vdd", "gnd"]:
for inst in self.insts:
self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name])
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
elif OPTS.route_supplies == "grid":
from router import supply_grid_router as router
else:
from router import supply_tree_router as router
rtr=router(layers=self.supply_stack,
design=self,
bbox=bbox,
pin_type=OPTS.supply_pin_type)
rtr.route()
if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Get new pins
pins = rtr.get_new_pins(pin_name)
for pin in pins:
self.add_layout_pin(self.ext_supply[pin_name],
pin.layer,
pin.ll(),
pin.width(),
pin.height())
elif OPTS.route_supplies and OPTS.supply_pin_type == "single":
# Update these as we may have routed outside the region (perimeter pins)
lowest_coord = self.find_lowest_coords()
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Get the lowest, leftest pin
pin = rtr.get_ll_pin(pin_name)
pin_width = 2 * getattr(self, "{}_width".format(pin.layer))
# Add it as an IO pin to the perimeter
route_width = pin.rx() - lowest_coord.x
pin_offset = vector(lowest_coord.x, pin.by())
self.add_rect(pin.layer,
pin_offset,
route_width,
pin.height())
self.add_layout_pin(self.ext_supply[pin_name],
pin.layer,
pin_offset,
pin_width,
pin.height())
else:
# Grid is left with many top level pins
pass
def route_escape_pins(self, bbox):
"""
Add the top-level pins for a single bank SRAM with control.
"""
# List of pin to new pin name
pins_to_route = []
for port in self.all_ports:
# Connect the control pins as inputs
for signal in self.control_logic_inputs[port]:
if signal.startswith("rbl"):
continue
if signal=="clk":
pins_to_route.append("{0}{1}".format(signal, port))
else:
pins_to_route.append("{0}{1}".format(signal, port))
if port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
pins_to_route.append("din{0}[{1}]".format(port, bit))
if port in self.readwrite_ports or port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
pins_to_route.append("dout{0}[{1}]".format(port, bit))
for bit in range(self.col_addr_size):
pins_to_route.append("addr{0}[{1}]".format(port, bit))
for bit in range(self.row_addr_size):
pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size))
if port in self.write_ports:
if self.write_size != self.word_size:
for bit in range(self.num_wmasks):
pins_to_route.append("wmask{0}[{1}]".format(port, bit))
if port in self.write_ports:
if self.num_spare_cols == 1:
pins_to_route.append("spare_wen{0}".format(port))
else:
for bit in range(self.num_spare_cols):
pins_to_route.append("spare_wen{0}[{1}]".format(port, bit))
from router import signal_escape_router as router
rtr=router(layers=self.m3_stack,
design=self,
bbox=bbox)
rtr.escape_route(pins_to_route)
def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """
# address size + control signals + one-hot bank select signals
self.num_vertical_line = self.bank_addr_size + self.control_size + 1# + log(self.num_banks, 2) + 1
# data bus size
self.num_horizontal_line = self.word_size
self.vertical_bus_width = self.m2_pitch * self.num_vertical_line
# vertical bus height depends on 2 or 4 banks
self.data_bus_height = self.m3_pitch * self.num_horizontal_line
self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
self.control_bus_height = self.m1_pitch * (self.control_size + 2)
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus
self.supply_bus_width = self.data_bus_width
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
debug.check(self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width,
"Bank is too small compared to control logic.")
def add_busses(self):
""" Add the horizontal and vertical busses """
# Vertical bus
# 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)]
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, pen])
else:
self.control_bus_names[port].extend([sen, wen, pen])
self.vert_control_bus_positions = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
names=self.control_bus_names[port],
length=self.vertical_bus_height)
self.addr_bus_names=["A{0}[{1}]".format(port, i) for i in range(self.bank_addr_size)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.addr_bus_offset,
names=self.addr_bus_names,
length=self.addr_bus_height))
# Horizontal data bus
self.data_bus_names = ["DATA{0}[{1}]".format(port, i) for i in range(self.word_size)]
self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3",
pitch=self.m3_pitch,
offset=self.data_bus_offset,
names=self.data_bus_names,
length=self.data_bus_width)
# Horizontal control logic bus
# vdd/gnd in bus go along whole SRAM
# FIXME: Fatten these wires?
self.horz_control_bus_positions = self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset,
names=["vdd"],
length=self.supply_bus_width)
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
# the decoder in 4-bank SRAMs
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset + vector(0, self.m1_pitch),
names=["gnd"],
length=self.supply_bus_width))
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.control_bus_offset,
names=self.control_bus_names[port],
length=self.control_bus_width))
def add_modules(self):
self.bitcell = factory.create(module_type=OPTS.bitcell)
self.dff = factory.create(module_type="dff")
# Create the bank module (up to four are instantiated)
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
self.num_spare_cols = self.bank.num_spare_cols
# Create the address and control flops (but not the clk)
self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1)
if self.col_addr_size > 0:
self.col_addr_dff = factory.create("dff_array", module_name="col_addr_dff", rows=1, columns=self.col_addr_size)
else:
self.col_addr_dff = None
self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size + self.num_spare_cols)
if self.write_size != self.word_size:
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
if self.num_spare_cols:
self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols)
self.bank_count = 0
c = reload(__import__('modules.' + OPTS.control_logic))
self.mod_control_logic = getattr(c, OPTS.control_logic)
# Create the control logic module for each port type
if len(self.readwrite_ports) > 0:
self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row,
word_size=self.word_size,
spare_columns=self.num_spare_cols,
sram=self,
port_type="rw")
if len(self.writeonly_ports) > 0:
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row,
word_size=self.word_size,
spare_columns=self.num_spare_cols,
sram=self,
port_type="w")
if len(self.readonly_ports) > 0:
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row,
word_size=self.word_size,
spare_columns=self.num_spare_cols,
sram=self,
port_type="r")
def create_bank(self, bank_num):
""" Create a bank """
self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num),
mod=self.bank))
temp = []
for port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
temp.append("dout{0}[{1}]".format(port, bit))
for port in self.all_ports:
temp.append("rbl_bl{0}".format(port))
for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
temp.append("bank_din{0}_{1}".format(port, bit))
for port in self.all_ports:
for bit in range(self.bank_addr_size):
temp.append("a{0}_{1}".format(port, bit))
for port in self.read_ports:
temp.append("s_en{0}".format(port))
for port in self.all_ports:
temp.append("p_en_bar{0}".format(port))
for port in self.write_ports:
temp.append("w_en{0}".format(port))
for bit in range(self.num_wmasks):
temp.append("bank_wmask{0}_{1}".format(port, bit))
for bit in range(self.num_spare_cols):
temp.append("bank_spare_wen{0}_{1}".format(port, bit))
for port in self.all_ports:
temp.append("wl_en{0}".format(port))
temp.extend(self.ext_supplies)
self.connect_inst(temp)
return self.bank_insts[-1]
def place_bank(self, bank_inst, position, x_flip, y_flip):
""" Place a bank at the given position with orientations """
# x_flip == 1 --> no flip in x_axis
# x_flip == -1 --> flip in x_axis
# y_flip == 1 --> no flip in y_axis
# y_flip == -1 --> flip in y_axis
# x_flip and y_flip are used for position translation
if x_flip == -1 and y_flip == -1:
bank_rotation = 180
else:
bank_rotation = 0
if x_flip == y_flip:
bank_mirror = "R0"
elif x_flip == -1:
bank_mirror = "MX"
elif y_flip == -1:
bank_mirror = "MY"
else:
bank_mirror = "R0"
bank_inst.place(offset=position,
mirror=bank_mirror,
rotate=bank_rotation)
return bank_inst
def create_row_addr_dff(self):
""" Add all address flops for the main decoder """
insts = []
for port in self.all_ports:
insts.append(self.add_inst(name="row_address{}".format(port),
mod=self.row_addr_dff))
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.row_addr_size):
inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size))
outputs.append("a{}_{}".format(port, bit + self.col_addr_size))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_col_addr_dff(self):
""" Add and place all address flops for the column decoder """
insts = []
for port in self.all_ports:
insts.append(self.add_inst(name="col_address{}".format(port),
mod=self.col_addr_dff))
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.col_addr_size):
inputs.append("addr{}[{}]".format(port, bit))
outputs.append("a{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_data_dff(self):
""" Add and place all data flops """
insts = []
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="data_dff{}".format(port),
mod=self.data_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.word_size + self.num_spare_cols):
inputs.append("din{}[{}]".format(port, bit))
outputs.append("bank_din{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_wmask_dff(self):
""" Add and place all wmask flops """
insts = []
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="wmask_dff{}".format(port),
mod=self.wmask_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.num_wmasks):
inputs.append("wmask{}[{}]".format(port, bit))
outputs.append("bank_wmask{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_spare_wen_dff(self):
""" Add all spare write enable flops """
insts = []
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="spare_wen_dff{}".format(port),
mod=self.spare_wen_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.num_spare_cols):
inputs.append("spare_wen{}[{}]".format(port, bit))
outputs.append("bank_spare_wen{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_control_logic(self):
""" Add control logic instances """
insts = []
for port in self.all_ports:
if port in self.readwrite_ports:
mod = self.control_logic_rw
elif port in self.write_ports:
mod = self.control_logic_w
else:
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))
temp.append("rbl_bl{}".format(port))
# Outputs
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.append("p_en_bar{}".format(port))
temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port)] + self.ext_supplies)
self.connect_inst(temp)
return insts
def sp_write(self, sp_name, lvs=False, trim=False):
# Write the entire spice of the object to the file
############################################################
# Spice circuit
############################################################
sp = open(sp_name, 'w')
sp.write("**************************************************\n")
sp.write("* OpenRAM generated memory.\n")
sp.write("* Words: {}\n".format(self.num_words))
sp.write("* Data bits: {}\n".format(self.word_size))
sp.write("* Banks: {}\n".format(self.num_banks))
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("* Trimmed: {}\n".format(trim))
sp.write("* LVS: {}\n".format(lvs))
sp.write("**************************************************\n")
# This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
# spice["gnd_name"]))
usedMODS = list()
self.sp_write_file(sp, usedMODS, lvs=lvs, trim=trim)
del usedMODS
sp.close()
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
self.bank.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bank.clear_exclude_bits()
def graph_exclude_column_mux(self, column_include_num, port):
"""
Excludes all columns muxes unrelated to the target bit being simulated.
"""
self.bank.graph_exclude_column_mux(column_include_num, port)
def graph_clear_column_mux(self, port):
"""
Clear mux exclusions to allow different bit tests.
"""
self.bank.graph_clear_column_mux(port)
def create_modules(self):
"""
@ -34,7 +772,7 @@ class sram_1bank(sram_base):
if self.col_addr_dff:
self.col_addr_dff_insts = self.create_col_addr_dff()
if self.write_size:
if self.write_size != self.word_size:
self.wmask_dff_insts = self.create_wmask_dff()
self.data_dff_insts = self.create_data_dff()
else:
@ -178,7 +916,7 @@ class sram_1bank(sram_base):
self.col_addr_pos[port] = vector(x_offset, 0)
if port in self.write_ports:
if self.write_size:
if self.write_size != self.word_size:
# Add the write mask flops below the write mask AND array.
self.wmask_pos[port] = vector(x_offset,
y_offset)
@ -230,7 +968,7 @@ class sram_1bank(sram_base):
self.spare_wen_dff_insts[port].place(self.spare_wen_pos[port], mirror="MX")
x_offset = self.spare_wen_dff_insts[port].lx()
if self.write_size:
if self.write_size != self.word_size:
# Add the write mask flops below the write mask AND array.
self.wmask_pos[port] = vector(x_offset - self.wmask_dff_insts[port].width,
y_offset)
@ -297,7 +1035,7 @@ class sram_1bank(sram_base):
start_layer=pin_layer)
if port in self.write_ports:
if self.write_size:
if self.write_size != self.word_size:
for bit in range(self.num_wmasks):
self.add_io_pin(self.wmask_dff_insts[port],
"din_{}".format(bit),
@ -609,7 +1347,7 @@ class sram_1bank(sram_base):
# Data dffs and wmask dffs are only for writing so are not useful for evaluating read delay.
for inst in self.data_dff_insts:
self.graph_inst_exclude.add(inst)
if self.write_size:
if self.write_size != self.word_size:
for inst in self.wmask_dff_insts:
self.graph_inst_exclude.add(inst)
if self.num_spare_cols:

View File

@ -1,240 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import sys
from tech import drc, spice
import debug
from math import log,sqrt,ceil
import datetime
import getpass
from base import vector
from globals import OPTS, print_time
from .sram_base import sram_base
from modules import bank
from modules import dff_buf_array
from modules import dff_array
class sram_2bank(sram_base):
"""
Procedures specific to a two 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 two bank SRAM """
# In 2 bank SRAM, the height is determined by the control bus which is higher than the msb address
self.vertical_bus_height = self.bank.height + 2*self.bank_to_bus_distance + self.data_bus_height + self.control_bus_height
# 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.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)
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 = self.control_logic_inst.uy()
def add_banks(self):
# Placement of bank 0 (left)
bank_position_0 = vector(self.bank.width,
self.bank.height)
self.bank_inst=[self.add_bank(0, bank_position_0, -1, -1)]
# Placement of bank 1 (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))
def add_logic(self):
""" Add the control and MSB logic """
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(self.addr_size-1)
self.connect_inst([self.msb_bank_sel_addr,"bank_sel[1]","bank_sel[0]","clk_buf", "vdd", "gnd"])
def route_shared_banks(self):
""" Route the shared signals for two and four bank configurations. """
# create the input control pins
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
# connect the control logic to the control bus
for n in self.control_logic_outputs + ["vdd", "gnd"]:
pins = self.control_logic_inst.get_pins(n)
for pin in pins:
if pin.layer=="m2":
pin_pos = pin.bc()
break
rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y)
self.add_path("m2",[pin_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
# connect the control logic cross bar
for n in self.control_logic_outputs:
cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y)
self.add_via_center(self.m1_stack,cross_pos)
# connect the bank select signals to the vertical bus
for i in range(self.num_banks):
pin = self.bank_inst[i].get_pin("bank_sel")
pin_pos = pin.rc() if i==0 else pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,pin_pos.y)
self.add_path("m3",[pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
def route_single_msb_address(self):
""" Route one MSB address bit for 2-bank SRAM """
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "m1": 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("m1",[vdd_pos,down_pos])
self.add_via_center(self.m1_stack,down_pos,rotate=90)
self.add_path("m2",[down_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
gnd_pins = self.msb_address_inst.get_pins("gnd")
# Only add the ground connection to the lowest metal2 rail in the flop array
# FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those
lowest_y = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "m2": continue
if lowest_y==None or gnd_pin.by()<lowest_y:
lowest_y=gnd_pin.by()
gnd_pos = gnd_pin.ur()
rail_pos = vector(gnd_pos.x,self.horz_control_bus_positions["gnd"].y)
self.add_path("m2",[gnd_pos,rail_pos])
self.add_via_center(self.m1_stack,rail_pos)
# connect the MSB flop to the address input bus
msb_pins = self.msb_address_inst.get_pins("din[0]")
for msb_pin in msb_pins:
if msb_pin.layer == "m3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr].x,msb_pin_pos.y)
self.add_path("m3",[msb_pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
# Connect the output bar to select 0
msb_out_pin = self.msb_address_inst.get_pin("dout_bar[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_up_pos = out_extend_right_pos + vector(0,self.m2_width)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[0]"].x,out_extend_up_pos.y)
self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_up_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
# Connect the output to select 1
msb_out_pin = self.msb_address_inst.get_pin("dout[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_down_pos = out_extend_right_pos - vector(0,2*self.m1_pitch)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[1]"].x,out_extend_down_pos.y)
self.add_path("m2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
self.add_wire(("m3","via2","m2"),[out_extend_right_pos,out_extend_down_pos,rail_pos])
self.add_via_center(self.m2_stack,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("m1",[clk_pos,bend_pos,rail_pos])
def route(self):
""" Route all of the signals for the two bank SRAM. """
self.route_shared_banks()
# connect the horizontal control bus to the vertical bus
# 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).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("m2",[pin_pos,rail_pos])
self.add_via_center(self.m2_stack,rail_pos)
self.route_single_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 == self.msb_bank_sel_addr: continue
pin0_pos = self.bank_inst[0].get_pin(n).rc()
pin1_pos = self.bank_inst[1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("m3",[pin0_pos,pin1_pos])
self.add_via_center(self.m2_stack,rail_pos)
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="m2",
offset=self.vert_control_bus_positions[n])
for n in self.bank_sel_bus_names:
self.add_label(text=n,
layer="m2",
offset=self.vert_control_bus_positions[n])

View File

@ -1,782 +0,0 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import datetime
import debug
from globals import OPTS, print_time
from math import log, ceil
import importlib
from base.vector import vector
from base.design import design
from base.verilog import verilog
from base.lef import lef
from sram_factory import factory
from tech import spice
class sram_base(design, verilog, lef):
"""
Dynamically generated SRAM by connecting banks to control logic. The
number of banks should be 1 , 2 or 4
"""
def __init__(self, name, sram_config):
design.__init__(self, name)
lef.__init__(self, ["m1", "m2", "m3", "m4"])
verilog.__init__(self)
self.sram_config = sram_config
sram_config.set_local_config(self)
self.bank_insts = []
if self.write_size:
self.num_wmasks = int(ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
if not self.num_spare_cols:
self.num_spare_cols = 0
def add_pins(self):
""" Add pins for entire SRAM. """
for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("din{0}[{1}]".format(port, bit), "INPUT")
for port in self.all_ports:
for bit in range(self.addr_size):
self.add_pin("addr{0}[{1}]".format(port, bit), "INPUT")
# These are used to create the physical pins
self.control_logic_inputs = []
self.control_logic_outputs = []
for port in self.all_ports:
if port in self.readwrite_ports:
self.control_logic_inputs.append(self.control_logic_rw.get_inputs())
self.control_logic_outputs.append(self.control_logic_rw.get_outputs())
elif port in self.write_ports:
self.control_logic_inputs.append(self.control_logic_w.get_inputs())
self.control_logic_outputs.append(self.control_logic_w.get_outputs())
else:
self.control_logic_inputs.append(self.control_logic_r.get_inputs())
self.control_logic_outputs.append(self.control_logic_r.get_outputs())
for port in self.all_ports:
self.add_pin("csb{}".format(port), "INPUT")
for port in self.readwrite_ports:
self.add_pin("web{}".format(port), "INPUT")
for port in self.all_ports:
self.add_pin("clk{}".format(port), "INPUT")
# add the optional write mask pins
for port in self.write_ports:
for bit in range(self.num_wmasks):
self.add_pin("wmask{0}[{1}]".format(port, bit), "INPUT")
if self.num_spare_cols == 1:
self.add_pin("spare_wen{0}".format(port), "INPUT")
else:
for bit in range(self.num_spare_cols):
self.add_pin("spare_wen{0}[{1}]".format(port, bit), "INPUT")
for port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
self.add_pin("dout{0}[{1}]".format(port, bit), "OUTPUT")
# Standard supply and ground names
try:
self.vdd_name = spice["power"]
except KeyError:
self.vdd_name = "vdd"
try:
self.gnd_name = spice["ground"]
except KeyError:
self.gnd_name = "gnd"
self.add_pin(self.vdd_name, "POWER")
self.add_pin(self.gnd_name, "GROUND")
self.ext_supplies = [self.vdd_name, self.gnd_name]
self.ext_supply = {"vdd" : self.vdd_name, "gnd" : self.gnd_name}
def add_global_pex_labels(self):
"""
Add pex labels at the sram level for spice analysis
"""
# add pex labels for bitcells
for bank_num in range(len(self.bank_insts)):
bank = self.bank_insts[bank_num]
pex_data = bank.reverse_transformation_bitcell(self.bitcell.name)
bank_offset = pex_data[0] # offset bank relative to sram
Q_offset = pex_data[1] # offset of storage relative to bank
Q_bar_offset = pex_data[2] # offset of storage relative to bank
bl_offsets = pex_data[3]
br_offsets = pex_data[4]
bl_meta = pex_data[5]
br_meta = pex_data[6]
bl = []
br = []
storage_layer_name = "m1"
bitline_layer_name = self.bitcell.get_pin("bl").layer
for cell in range(len(bank_offset)):
Q = [bank_offset[cell][0] + Q_offset[cell][0],
bank_offset[cell][1] + Q_offset[cell][1]]
Q_bar = [bank_offset[cell][0] + Q_bar_offset[cell][0],
bank_offset[cell][1] + Q_bar_offset[cell][1]]
OPTS.words_per_row = self.words_per_row
row = int(cell % (OPTS.num_words / self.words_per_row))
col = int(cell / (OPTS.num_words))
self.add_layout_pin_rect_center("bitcell_Q_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q)
self.add_layout_pin_rect_center("bitcell_Q_bar_b{}_r{}_c{}".format(bank_num,
row,
col),
storage_layer_name,
Q_bar)
for cell in range(len(bl_offsets)):
col = bl_meta[cell][0][2]
for bitline in range(len(bl_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + bl_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + bl_offsets[cell][bitline][1]]
bl.append([bitline_location, bl_meta[cell][bitline][3], col])
for cell in range(len(br_offsets)):
col = br_meta[cell][0][2]
for bitline in range(len(br_offsets[cell])):
bitline_location = [float(bank_offset[cell][0]) + br_offsets[cell][bitline][0],
float(bank_offset[cell][1]) + br_offsets[cell][bitline][1]]
br.append([bitline_location, br_meta[cell][bitline][3], col])
for i in range(len(bl)):
self.add_layout_pin_rect_center("bl{0}_{1}".format(bl[i][1], bl[i][2]),
bitline_layer_name, bl[i][0])
for i in range(len(br)):
self.add_layout_pin_rect_center("br{0}_{1}".format(br[i][1], br[i][2]),
bitline_layer_name, br[i][0])
# add pex labels for control logic
for i in range(len(self.control_logic_insts)):
instance = self.control_logic_insts[i]
control_logic_offset = instance.offset
for output in instance.mod.output_list:
pin = instance.mod.get_pin(output)
pin.transform([0, 0], instance.mirror, instance.rotate)
offset = [control_logic_offset[0] + pin.center()[0],
control_logic_offset[1] + pin.center()[1]]
self.add_layout_pin_rect_center("{0}{1}".format(pin.name, i),
storage_layer_name,
offset)
def create_netlist(self):
""" Netlist creation """
start_time = datetime.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.datetime.now(), start_time)
def create_layout(self):
""" Layout creation """
start_time = datetime.datetime.now()
self.place_instances()
if not OPTS.is_unit_test:
print_time("Placement", datetime.datetime.now(), start_time)
start_time = datetime.datetime.now()
self.route_layout()
if not OPTS.is_unit_test:
print_time("Routing", datetime.datetime.now(), start_time)
self.add_lvs_correspondence_points()
self.offset_all_coordinates()
highest_coord = self.find_highest_coords()
self.width = highest_coord[0]
self.height = highest_coord[1]
if OPTS.use_pex and OPTS.pex_exe[0] != "calibre":
debug.info(2, "adding global pex labels")
self.add_global_pex_labels()
self.add_boundary(ll=vector(0, 0),
ur=vector(self.width, self.height))
start_time = datetime.datetime.now()
if not OPTS.is_unit_test:
# We only enable final verification if we have routed the design
# Only run this if not a unit test, because unit test will also verify it.
self.DRC_LVS(final_verification=OPTS.route_supplies, force_check=OPTS.check_lvsdrc)
print_time("Verification", datetime.datetime.now(), start_time)
def create_modules(self):
debug.error("Must override pure virtual function.", -1)
def route_supplies(self, bbox=None):
""" Route the supply grid and connect the pins to them. """
# Copy the pins to the top level
# This will either be used to route or left unconnected.
for pin_name in ["vdd", "gnd"]:
for inst in self.insts:
self.copy_power_pins(inst, pin_name, self.ext_supply[pin_name])
# Pick the router type
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
elif OPTS.route_supplies == "grid":
from router import supply_grid_router as router
else:
from router import supply_tree_router as router
rtr=router(layers=self.supply_stack,
design=self,
bbox=bbox,
pin_type=OPTS.supply_pin_type)
rtr.route()
# This removes the original pre-supply routing pins and replaces them
# with the ring or peripheral power pins
if OPTS.supply_pin_type in ["left", "right", "top", "bottom", "ring"]:
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Get new pins
pins = rtr.get_new_pins(pin_name)
for pin in pins:
self.add_layout_pin(self.ext_supply[pin_name],
pin.layer,
pin.ll(),
pin.width(),
pin.height())
elif OPTS.route_supplies and OPTS.supply_pin_type == "single":
# Update these as we may have routed outside the region (perimeter pins)
lowest_coord = self.find_lowest_coords()
# Find the lowest leftest pin for vdd and gnd
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.get_pins(pin_name):
self.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.remove_layout_pin(pin_name)
# Get the lowest, leftest pin
pin = rtr.get_ll_pin(pin_name)
pin_width = 2 * getattr(self, "{}_width".format(pin.layer))
# Add it as an IO pin to the perimeter
route_width = pin.rx() - lowest_coord.x
pin_offset = vector(lowest_coord.x, pin.by())
self.add_rect(pin.layer,
pin_offset,
route_width,
pin.height())
self.add_layout_pin(self.ext_supply[pin_name],
pin.layer,
pin_offset,
pin_width,
pin.height())
else:
# Grid is left with many top level pins
pass
def route_escape_pins(self, bbox):
"""
Add the top-level pins for a single bank SRAM with control.
"""
# List of pin to new pin name
pins_to_route = []
for port in self.all_ports:
# Connect the control pins as inputs
for signal in self.control_logic_inputs[port]:
if signal.startswith("rbl"):
continue
if signal=="clk":
pins_to_route.append("{0}{1}".format(signal, port))
else:
pins_to_route.append("{0}{1}".format(signal, port))
if port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
pins_to_route.append("din{0}[{1}]".format(port, bit))
if port in self.readwrite_ports or port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
pins_to_route.append("dout{0}[{1}]".format(port, bit))
for bit in range(self.col_addr_size):
pins_to_route.append("addr{0}[{1}]".format(port, bit))
for bit in range(self.row_addr_size):
pins_to_route.append("addr{0}[{1}]".format(port, bit + self.col_addr_size))
if port in self.write_ports:
if self.write_size:
for bit in range(self.num_wmasks):
pins_to_route.append("wmask{0}[{1}]".format(port, bit))
if port in self.write_ports:
if self.num_spare_cols == 1:
pins_to_route.append("spare_wen{0}".format(port))
else:
for bit in range(self.num_spare_cols):
pins_to_route.append("spare_wen{0}[{1}]".format(port, bit))
from router import signal_escape_router as router
rtr=router(layers=self.m3_stack,
design=self,
bbox=bbox)
rtr.escape_route(pins_to_route)
def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """
# address size + control signals + one-hot bank select signals
self.num_vertical_line = self.addr_size + self.control_size + log(self.num_banks, 2) + 1
# data bus size
self.num_horizontal_line = self.word_size
self.vertical_bus_width = self.m2_pitch * self.num_vertical_line
# vertical bus height depends on 2 or 4 banks
self.data_bus_height = self.m3_pitch * self.num_horizontal_line
self.data_bus_width = 2 * (self.bank.width + self.bank_to_bus_distance) + self.vertical_bus_width
self.control_bus_height = self.m1_pitch * (self.control_size + 2)
self.control_bus_width = self.bank.width + self.bank_to_bus_distance + self.vertical_bus_width
self.supply_bus_height = self.m1_pitch * 2 # 2 for vdd/gnd placed with control bus
self.supply_bus_width = self.data_bus_width
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
debug.check(self.bank.width + self.vertical_bus_width > 0.9 * self.control_logic.width,
"Bank is too small compared to control logic.")
def add_busses(self):
""" Add the horizontal and vertical busses """
# Vertical bus
# 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)]
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, pen])
else:
self.control_bus_names[port].extend([sen, wen, pen])
self.vert_control_bus_positions = self.create_vertical_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.vertical_bus_offset,
names=self.control_bus_names[port],
length=self.vertical_bus_height)
self.addr_bus_names=["A{0}[{1}]".format(port, i) for i in range(self.addr_size)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.addr_bus_offset,
names=self.addr_bus_names,
length=self.addr_bus_height))
self.bank_sel_bus_names = ["bank_sel{0}_{1}".format(port, i) for i in range(self.num_banks)]
self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names,
length=self.vertical_bus_height))
# Horizontal data bus
self.data_bus_names = ["DATA{0}[{1}]".format(port, i) for i in range(self.word_size)]
self.data_bus_positions = self.create_horizontal_pin_bus(layer="m3",
pitch=self.m3_pitch,
offset=self.data_bus_offset,
names=self.data_bus_names,
length=self.data_bus_width)
# Horizontal control logic bus
# vdd/gnd in bus go along whole SRAM
# FIXME: Fatten these wires?
self.horz_control_bus_positions = self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset,
names=["vdd"],
length=self.supply_bus_width)
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
# the decoder in 4-bank SRAMs
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.supply_bus_offset + vector(0, self.m1_pitch),
names=["gnd"],
length=self.supply_bus_width))
self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=self.control_bus_offset,
names=self.control_bus_names[port],
length=self.control_bus_width))
def add_multi_bank_modules(self):
""" Create the multibank address flops and bank decoder """
from dff_buf_array import dff_buf_array
self.msb_address = dff_buf_array(name="msb_address",
rows=1,
columns=self.num_banks / 2)
if self.num_banks>2:
self.msb_decoder = self.bank.decoder.pre2_4
def add_modules(self):
self.bitcell = factory.create(module_type=OPTS.bitcell)
self.dff = factory.create(module_type="dff")
# Create the bank module (up to four are instantiated)
self.bank = factory.create("bank", sram_config=self.sram_config, module_name="bank")
self.num_spare_cols = self.bank.num_spare_cols
# Create the address and control flops (but not the clk)
self.row_addr_dff = factory.create("dff_array", module_name="row_addr_dff", rows=self.row_addr_size, columns=1)
if self.col_addr_size > 0:
self.col_addr_dff = factory.create("dff_array", module_name="col_addr_dff", rows=1, columns=self.col_addr_size)
else:
self.col_addr_dff = None
self.data_dff = factory.create("dff_array", module_name="data_dff", rows=1, columns=self.word_size + self.num_spare_cols)
if self.write_size:
self.wmask_dff = factory.create("dff_array", module_name="wmask_dff", rows=1, columns=self.num_wmasks)
if self.num_spare_cols:
self.spare_wen_dff = factory.create("dff_array", module_name="spare_wen_dff", rows=1, columns=self.num_spare_cols)
# Create bank decoder
if(self.num_banks > 1):
self.add_multi_bank_modules()
self.bank_count = 0
c = importlib.import_module("modules." + OPTS.control_logic)
self.mod_control_logic = getattr(c, OPTS.control_logic)
# Create the control logic module for each port type
if len(self.readwrite_ports) > 0:
self.control_logic_rw = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row,
word_size=self.word_size,
spare_columns=self.num_spare_cols,
sram=self,
port_type="rw")
if len(self.writeonly_ports) > 0:
self.control_logic_w = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row,
word_size=self.word_size,
spare_columns=self.num_spare_cols,
sram=self,
port_type="w")
if len(self.readonly_ports) > 0:
self.control_logic_r = self.mod_control_logic(num_rows=self.num_rows,
words_per_row=self.words_per_row,
word_size=self.word_size,
spare_columns=self.num_spare_cols,
sram=self,
port_type="r")
def create_bank(self, bank_num):
""" Create a bank """
self.bank_insts.append(self.add_inst(name="bank{0}".format(bank_num),
mod=self.bank))
temp = []
for port in self.read_ports:
for bit in range(self.word_size + self.num_spare_cols):
temp.append("dout{0}[{1}]".format(port, bit))
for port in self.all_ports:
temp.append("rbl_bl{0}".format(port))
for port in self.write_ports:
for bit in range(self.word_size + self.num_spare_cols):
temp.append("bank_din{0}_{1}".format(port, bit))
for port in self.all_ports:
for bit in range(self.bank_addr_size):
temp.append("a{0}_{1}".format(port, bit))
if(self.num_banks > 1):
for port in self.all_ports:
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.all_ports:
temp.append("p_en_bar{0}".format(port))
for port in self.write_ports:
temp.append("w_en{0}".format(port))
for bit in range(self.num_wmasks):
temp.append("bank_wmask{0}_{1}".format(port, bit))
for bit in range(self.num_spare_cols):
temp.append("bank_spare_wen{0}_{1}".format(port, bit))
for port in self.all_ports:
temp.append("wl_en{0}".format(port))
temp.extend(self.ext_supplies)
self.connect_inst(temp)
return self.bank_insts[-1]
def place_bank(self, bank_inst, position, x_flip, y_flip):
""" Place a bank at the given position with orientations """
# x_flip == 1 --> no flip in x_axis
# x_flip == -1 --> flip in x_axis
# y_flip == 1 --> no flip in y_axis
# y_flip == -1 --> flip in y_axis
# x_flip and y_flip are used for position translation
if x_flip == -1 and y_flip == -1:
bank_rotation = 180
else:
bank_rotation = 0
if x_flip == y_flip:
bank_mirror = "R0"
elif x_flip == -1:
bank_mirror = "MX"
elif y_flip == -1:
bank_mirror = "MY"
else:
bank_mirror = "R0"
bank_inst.place(offset=position,
mirror=bank_mirror,
rotate=bank_rotation)
return bank_inst
def create_row_addr_dff(self):
""" Add all address flops for the main decoder """
insts = []
for port in self.all_ports:
insts.append(self.add_inst(name="row_address{}".format(port),
mod=self.row_addr_dff))
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.row_addr_size):
inputs.append("addr{}[{}]".format(port, bit + self.col_addr_size))
outputs.append("a{}_{}".format(port, bit + self.col_addr_size))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_col_addr_dff(self):
""" Add and place all address flops for the column decoder """
insts = []
for port in self.all_ports:
insts.append(self.add_inst(name="col_address{}".format(port),
mod=self.col_addr_dff))
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.col_addr_size):
inputs.append("addr{}[{}]".format(port, bit))
outputs.append("a{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_data_dff(self):
""" Add and place all data flops """
insts = []
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="data_dff{}".format(port),
mod=self.data_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.word_size + self.num_spare_cols):
inputs.append("din{}[{}]".format(port, bit))
outputs.append("bank_din{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_wmask_dff(self):
""" Add and place all wmask flops """
insts = []
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="wmask_dff{}".format(port),
mod=self.wmask_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.num_wmasks):
inputs.append("wmask{}[{}]".format(port, bit))
outputs.append("bank_wmask{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_spare_wen_dff(self):
""" Add all spare write enable flops """
insts = []
for port in self.all_ports:
if port in self.write_ports:
insts.append(self.add_inst(name="spare_wen_dff{}".format(port),
mod=self.spare_wen_dff))
else:
insts.append(None)
continue
# inputs, outputs/output/bar
inputs = []
outputs = []
for bit in range(self.num_spare_cols):
if self.num_spare_cols == 1:
inputs.append("spare_wen{0}".format(port))
else:
inputs.append("spare_wen{0}[{1}]".format(port, bit))
outputs.append("bank_spare_wen{}_{}".format(port, bit))
self.connect_inst(inputs + outputs + ["clk_buf{}".format(port)] + self.ext_supplies)
return insts
def create_control_logic(self):
""" Add control logic instances """
insts = []
for port in self.all_ports:
if port in self.readwrite_ports:
mod = self.control_logic_rw
elif port in self.write_ports:
mod = self.control_logic_w
else:
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))
temp.append("rbl_bl{}".format(port))
# Outputs
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.append("p_en_bar{}".format(port))
temp.extend(["wl_en{}".format(port), "clk_buf{}".format(port)] + self.ext_supplies)
self.connect_inst(temp)
return insts
def sp_write(self, sp_name, lvs=False, trim=False):
# Write the entire spice of the object to the file
############################################################
# Spice circuit
############################################################
sp = open(sp_name, 'w')
sp.write("**************************************************\n")
sp.write("* OpenRAM generated memory.\n")
sp.write("* Words: {}\n".format(self.num_words))
sp.write("* Data bits: {}\n".format(self.word_size))
sp.write("* Banks: {}\n".format(self.num_banks))
sp.write("* Column mux: {}:1\n".format(self.words_per_row))
sp.write("* Trimmed: {}\n".format(trim))
sp.write("* LVS: {}\n".format(lvs))
sp.write("**************************************************\n")
# This causes unit test mismatch
# sp.write("* Created: {0}\n".format(datetime.datetime.now()))
# sp.write("* User: {0}\n".format(getpass.getuser()))
# sp.write(".global {0} {1}\n".format(spice["vdd_name"],
# spice["gnd_name"]))
usedMODS = list()
self.sp_write_file(sp, usedMODS, lvs=lvs, trim=trim)
del usedMODS
sp.close()
def graph_exclude_bits(self, targ_row, targ_col):
"""
Excludes bits in column from being added to graph except target
"""
self.bank.graph_exclude_bits(targ_row, targ_col)
def clear_exclude_bits(self):
"""
Clears the bit exclusions
"""
self.bank.clear_exclude_bits()
def graph_exclude_column_mux(self, column_include_num, port):
"""
Excludes all columns muxes unrelated to the target bit being simulated.
"""
self.bank.graph_exclude_column_mux(column_include_num, port)
def graph_clear_column_mux(self, port):
"""
Clear mux exclusions to allow different bit tests.
"""
self.bank.graph_clear_column_mux(port)

View File

@ -18,10 +18,11 @@ class sram_config:
self.word_size = word_size
self.num_words = num_words
# Don't add a write mask if it is the same size as the data word
if write_size and write_size==word_size:
self.write_size = None
else:
self.write_size_init = write_size
if write_size:
self.write_size = write_size
else:
self.write_size = word_size
self.num_banks = num_banks
self.num_spare_rows = num_spare_rows
self.num_spare_cols = num_spare_cols
@ -72,8 +73,8 @@ class sram_config:
bitcell = factory.create(module_type=OPTS.bitcell)
debug.check(self.num_banks in [1, 2, 4],
"Valid number of banks are 1 , 2 and 4.")
debug.check(ceil(log(self.num_banks, 2)) == log(self.num_banks, 2) ,
"Number of banks should be power of 2.")
self.num_words_per_bank = self.num_words / self.num_banks
self.num_bits_per_bank = self.word_size * self.num_words_per_bank
@ -117,11 +118,18 @@ class sram_config:
self.num_rows = self.num_rows_temp + self.num_spare_rows
debug.info(1, "Rows: {} Cols: {}".format(self.num_rows_temp, self.num_cols))
# Fix the write_size
if self.write_size_init:
self.write_size = self.write_size_init
else:
self.write_size = self.word_size
# Compute the address and bank sizes
self.row_addr_size = ceil(log(self.num_rows, 2))
self.col_addr_size = int(log(self.words_per_row, 2))
self.bank_addr_size = self.col_addr_size + self.row_addr_size
self.addr_size = self.bank_addr_size + int(log(self.num_banks, 2))
#self.addr_size = self.bank_addr_size
debug.info(1, "Row addr size: {}".format(self.row_addr_size)
+ " Col addr size: {}".format(self.col_addr_size)
+ " Bank addr size: {}".format(self.bank_addr_size))

View File

@ -0,0 +1,40 @@
from .template import template
from globals import OPTS
import os
from math import ceil, log
import re
class sram_multibank:
def __init__(self, sram):
rw_ports = [i for i in sram.all_ports if i in sram.read_ports and i in sram.write_ports]
r_ports = [i for i in sram.all_ports if i in sram.read_ports and i not in sram.write_ports]
w_ports = [i for i in sram.all_ports if i not in sram.read_ports and i in sram.write_ports]
self.dict = {
'module_name': sram.name + '_top',
'bank_module_name': sram.name,
'vdd': 'vdd',
'gnd': 'gnd',
'ports': sram.all_ports,
'rw_ports': rw_ports,
'r_ports': r_ports,
'w_ports': w_ports,
'banks': list(range(sram.num_banks)),
'data_width': sram.word_size,
'addr_width': sram.bank_addr_size + ceil(log(sram.num_banks, 2)),
'bank_sel': ceil(log(sram.num_banks, 2)),
'num_wmask': sram.num_wmasks,
'write_size': sram.write_size
}
def verilog_write(self, name):
template_filename = os.path.join(os.path.abspath(os.environ["OPENRAM_HOME"]), "modules/sram_multibank_template.v")
t = template(template_filename, self.dict)
t.write(name)
with open(name, 'r') as f:
text = f.read()
badComma = re.compile(r',(\s*\n\s*\);)')
text = badComma.sub(r'\1', text)
with open(name, 'w') as f:
f.write(text)

View File

@ -0,0 +1,189 @@
module {{ module_name }} (
`ifdef USE_POWER_PINS
{{ vdd }},
{{ gnd }},
`endif
{% for port in rw_ports %}
clk{{ port }},
addr{{ port }},
din{{ port }},
csb{{ port }},
{% if num_wmask > 1 %}
wmask{{ port }},
{% endif %}
web{{ port }},
dout{{ port }},
{% endfor %}
{% for port in r_ports %}
clk{{ port }},
addr{{ port }},
csb{{ port }},
dout{{ port }},
{% endfor %}
{% for port in w_ports %}
clk{{ port }},
addr{{ port }},
din{{ port }},
csb{{ port }},
{% if num_wmask > 1 %}
wmask{{ port }},
{% endif %}
web{{ port }},
{% endfor %}
);
parameter DATA_WIDTH = {{ data_width }};
parameter ADDR_WIDTH= {{ addr_width }};
parameter BANK_SEL = {{ bank_sel }};
parameter NUM_WMASK = {{ num_wmask }};
`ifdef USE_POWER_PINS
inout {{ vdd }};
inout {{ gnd }};
`endif
{% for port in rw_ports %}
input clk{{ port }};
input [ADDR_WIDTH - 1 : 0] addr{{ port }};
input [DATA_WIDTH - 1: 0] din{{ port }};
input csb{{ port }};
input web{{ port }};
{% if num_wmask > 1 %}
input [NUM_WMASK - 1 : 0] wmask{{ port }};
{% endif %}
output reg [DATA_WIDTH - 1 : 0] dout{{ port }};
{% endfor %}
{% for port in r_ports %}
input clk{{ port }};
input [ADDR_WIDTH - 1 : 0] addr{{ port }};
input csb{{ port }};
output reg [DATA_WIDTH - 1 : 0] dout{{ port }};
{% endfor %}
{% for port in w_ports %}
input clk{{ port }};
input [ADDR_WIDTH - 1 : 0] addr{{ port }};
input [DATA_WIDTH - 1: 0] din{{ port }};
input csb{{ port }};
input web{{ port }};
{% if num_wmask > 1 %}
input [NUM_WMASK - 1 : 0] wmask{{ port }};
{% endif %}
{% endfor %}
{% for port in ports %}
reg [BANK_SEL - 1 : 0] addr{{ port }}_reg;
{% for bank in banks %}
wire [DATA_WIDTH - 1 : 0] dout{{ port }}_bank{{ bank }};
reg web{{ port }}_bank{{ bank }};
reg csb{{ port }}_bank{{ bank }};
{% endfor %}
{% endfor %}
{% for bank in banks %}
{{ bank_module_name }} bank{{ bank }} (
`ifdef USE_POWER_PINS
.{{ vdd }}({{ vdd }}),
.{{ gnd }}({{ gnd }}),
`endif
{% for port in rw_ports %}
.clk{{ port }}(clk{{ port }}),
.addr{{ port }}(addr{{ port }}[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din{{ port }}(din{{ port }}),
.csb{{ port }}(csb{{ port }}_bank{{ bank }}),
.web{{ port }}(web{{ port }}_bank{{ bank }}),
{% if num_wmask > 1 %}
.wmask{{ port }}(wmask{{ port }}),
{% endif %}
.dout{{ port }}(dout{{ port }}_bank{{ bank }}),
{% endfor %}
{% for port in r_ports %}
.clk{{ port }}(clk{{ port }}),
.addr{{ port }}(addr{{ port }}[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.csb{{ port }}(csb{{ port }}_bank{{ bank }}),
.dout{{ port }}(dout{{ port }}_bank{{ bank }}),
{% endfor %}
{% for port in w_ports %}
.clk{{ port }}(clk{{ port }}),
.addr{{ port }}(addr{{ port }}[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din{{ port }}(din{{ port }}),
.csb{{ port }}(csb{{ port }}_bank{{ bank }}),
{% if num_wmask > 1 %}
.wmask{{ port }}(wmask{{ port }}),
{% endif %}
.web{{ port }}(web{{ port }}_bank{{ bank }}),
{% endfor %}
);
{% endfor %}
{% for port in ports %}
always @(posedge clk{{ port }}) begin
addr{{ port }}_reg <= addr{{ port }}[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL];
end
{% endfor %}
{% for port in ports %}
always @(*) begin
case (addr{{ port }}_reg)
{% for bank in banks %}
{{ bank }}: begin
dout{{ port }} = dout{{ port }}_bank{{ bank }};
end
{% endfor %}
endcase
end
{% endfor %}
{% for port in rw_ports %}
always @(*) begin
{% for bank in banks %}
csb{{ port }}_bank{{ bank }} = 1'b1;
web{{ port }}_bank{{ bank }} = 1'b1;
{% endfor %}
case (addr{{ port }}[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
{% for bank in banks %}
{{ bank }}: begin
web{{ port }}_bank{{ bank }} = web{{ port }};
csb{{ port }}_bank{{ bank }} = csb{{ port }};
end
{% endfor %}
endcase
end
{% endfor %}
{% for port in w_ports %}
always @(*) begin
{% for bank in banks %}
csb{{ port }}_bank{{ bank }} = 1'b1;
web{{ port }}_bank{{ bank }} = 1'b1;
{% endfor %}
case (addr{{ port }}[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
{% for bank in banks %}
{{ bank }}: begin
web{{ port }}_bank{{ bank }} = web{{ port }};
csb{{ port }}_bank{{ bank }} = csb{{ port }};
end
{% endfor %}
endcase
end
{% endfor %}
{% for port in r_ports %}
always @(*) begin
{% for bank in banks %}
csb{{ port }}_bank{{ bank }} = 1'b1;
{% endfor %}
case (addr{{ port }}[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
{% for bank in banks %}
{{ bank }}: begin
csb{{ port }}_bank{{ bank }} = csb{{ port }};
end
{% endfor %}
endcase
end
{% endfor %}
endmodule

View File

@ -0,0 +1,120 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California
# Santa Cruz
# All rights reserved.
#
import re
class baseSection:
"""
This is the base section class for other section classes to inherit.
It is also used as the top most section.
"""
def __init__(self):
self.children = []
def expand(self, dict, fd):
for c in self.children:
c.expand(dict, fd)
class loopSection(baseSection):
"""
This section is for looping elements. It will repeat the children
sections based on the key list.
"""
def __init__(self, var, key):
baseSection.__init__(self)
self.var = var
self.key = key
def expand(self, dict, fd):
for ind in dict[self.key]:
dict[self.var] = ind
for c in self.children:
c.expand(dict, fd)
if self.var in dict:
del dict[self.var]
class conditionalSection(baseSection):
"""
This class will conditionally print it's children based on the 'cond'
element.
"""
def __init__(self, cond):
baseSection.__init__(self)
self.cond = cond
def expand(self, dict, fd):
run = eval(self.cond, dict)
if run:
for c in self.children:
c.expand(dict, fd)
class textSection(baseSection):
"""
This is plain text section. It can contain parameters that can be
replaced based on the dictionary.
"""
def __init__(self, text):
self.text = text
def expand(self, dict, fd):
varRE = re.compile(r'\{\{ (\S*) \}\}')
vars = varRE.finditer(self.text)
newText = self.text
for var in vars:
newText = newText.replace('{{ ' + var.group(1) + ' }}', str(dict[var.group(1)]))
fd.write(newText)
class template:
"""
The template class will read a template and generate an output file
based on the template and the given dictionary.
"""
def __init__(self, template, dict):
self.template = template
self.dict = dict
def readTemplate(self):
lines = []
with open(self.template, 'r') as f:
lines = f.readlines()
self.baseSectionSection = baseSection()
context = [self.baseSectionSection]
forRE = re.compile(r'\s*\{% for (\S*) in (\S*) %\}')
endforRE = re.compile(r'\s*\{% endfor %\}')
ifRE = re.compile(r'\s*{% if (.*) %\}')
endifRE = re.compile(r'\s*\{% endif %\}')
for line in lines:
m = forRE.match(line)
if m:
section = loopSection(m.group(1), m.group(2))
context[-1].children.append(section)
context.append(section)
continue
m = ifRE.match(line)
if m:
section = conditionalSection(m.group(1))
context[-1].children.append(section)
context.append(section)
continue
if endforRE.match(line) or endifRE.match(line):
context.pop()
else:
context[-1].children.append(textSection(line))
def write(self, filename):
fd = open(filename, 'w')
self.readTemplate()
self.baseSectionSection.expand(self.dict, fd)
fd.close()

View File

@ -29,7 +29,10 @@ class write_driver_array(design):
self.columns = columns
self.word_size = word_size
self.write_size = write_size
if write_size is None:
self.write_size = word_size
else:
self.write_size = write_size
self.offsets = offsets
self.column_offset = column_offset
self.words_per_row = int(columns / word_size)
@ -38,8 +41,10 @@ class write_driver_array(design):
else:
self.num_spare_cols = num_spare_cols
if self.write_size:
if self.write_size != self.word_size:
self.num_wmasks = int(math.ceil(self.word_size / self.write_size))
else:
self.num_wmasks = 0
self.create_netlist()
if not OPTS.netlist_only:
@ -82,10 +87,10 @@ class write_driver_array(design):
for i in range(self.word_size + self.num_spare_cols):
self.add_pin(self.get_bl_name() + "_{0}".format(i), "OUTPUT")
self.add_pin(self.get_br_name() + "_{0}".format(i), "OUTPUT")
if self.write_size:
if self.write_size != self.word_size:
for i in range(self.num_wmasks + self.num_spare_cols):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
elif self.num_spare_cols and not self.write_size:
elif self.num_spare_cols and self.write_size == self.word_size:
for i in range(self.num_spare_cols + 1):
self.add_pin(self.en_name + "_{0}".format(i), "INPUT")
else:
@ -110,7 +115,7 @@ class write_driver_array(design):
self.local_insts.append(self.add_inst(name=name,
mod=self.driver))
if self.write_size:
if self.write_size != self.word_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
@ -121,7 +126,7 @@ class write_driver_array(design):
w = 0
windex+=1
elif self.num_spare_cols and not self.write_size:
elif self.num_spare_cols and self.write_size == self.word_size:
self.connect_inst([self.data_name + "_{0}".format(index),
self.get_bl_name() + "_{0}".format(index),
self.get_br_name() + "_{0}".format(index),
@ -135,7 +140,7 @@ class write_driver_array(design):
for i in range(self.num_spare_cols):
index = self.word_size + i
if self.write_size:
if self.write_size != self.word_size:
offset = self.num_wmasks
else:
offset = 1
@ -205,7 +210,7 @@ class write_driver_array(design):
width=br_pin.width(),
height=br_pin.height())
if self.write_size:
if self.write_size != self.word_size:
for bit in range(self.num_wmasks):
inst = self.local_insts[bit * self.write_size]
en_pin = inst.get_pin(inst.mod.en_name)
@ -229,7 +234,7 @@ class write_driver_array(design):
layer="m1",
offset=en_pin.lr() + vector(-drc("minwidth_m1"),0))
elif self.num_spare_cols and not self.write_size:
elif self.num_spare_cols and self.write_size == self.word_size:
# shorten enable rail to accomodate those for spare write drivers
left_inst = self.local_insts[0]
left_en_pin = left_inst.get_pin(inst.mod.en_name)

View File

@ -805,7 +805,7 @@ class router(router_tech):
try:
x = track[0]*self.track_width - 0.5*self.track_width
except TypeError:
print(track[0], type(track[0]), self.track_width, type(self.track_width))
debug.warning("{} {} {} {}".format(track[0], type(track[0]), self.track_width, type(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))

View File

@ -145,13 +145,14 @@ class supply_tree_router(router):
connections.append((x, y))
# Route MST components
level=99
for index, (src, dest) in enumerate(connections):
if not (index % 25):
debug.info(1, "{0} supply segments routed, {1} remaining.".format(index, len(connections) - index))
self.route_signal(pin_name, src, dest)
if False and pin_name == "gnd":
print("\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
print("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages))
debug.info(level, "\nSRC {}: ".format(src) + str(self.pin_groups[pin_name][src].grids) + str(self.pin_groups[pin_name][src].blockages))
debug.info(level, ("DST {}: ".format(dest) + str(self.pin_groups[pin_name][dest].grids) + str(self.pin_groups[pin_name][dest].blockages)))
self.write_debug_gds("post_{0}_{1}.gds".format(src, dest), False)
#self.write_debug_gds("final_tree_router_{}.gds".format(pin_name), False)

View File

@ -1,5 +0,0 @@
from .sram_1bank import *
from .sram_2bank import *
from .sram_base import *
from .sram_config import *
from .sram import *

View File

@ -125,6 +125,11 @@ def check_file_format_whitespace(file_name):
def check_print_output(file_name):
"""Check if any files (except debug.py) call the _print_ function. We should
use the debug output with verbosity instead!"""
skip_files = ["printGDS.py", "uniquifyGDS.py", "processGDS.py", "model_data_util.py"]
base_file_name = os.path.basename(file_name)
if base_file_name in skip_files:
return(0)
file = open(file_name, "r+b")
line = file.read().decode('utf-8')
# skip comments with a hash

View File

@ -92,8 +92,8 @@ class model_delay_test(openram_test):
else:
self.assertTrue(False) # other techs fail
print('spice_delays', spice_delays)
print('model_delays', model_delays)
debug.info(3, 'spice_delays {}'.fomrat(spice_delays))
debug.info(3, 'model_delays {}'.format(model_delays))
# Check if no too many or too few results
self.assertTrue(len(spice_delays.keys())==len(model_delays.keys()))

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2021 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
import debug
class multibank_verilog_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
OPTS.route_supplies=False
OPTS.check_lvsdrc=False
OPTS.netlist_only=True
from modules import sram
from modules import sram_config
c = sram_config(word_size=2,
num_words=16,
num_banks=2)
c.words_per_row=1
c.recompute_sizes()
debug.info(1, "Testing Verilog for sample 2 bit, 16 words SRAM with 2 bank")
# This doesn't have to use the factory since worst case
# it will just replaece the top-level module of the same name
s = sram(c, "sram_2_16_2_{0}".format(OPTS.tech_name))
vfile = s.name + "_top.v"
vname = OPTS.openram_temp + vfile
v1bfile = s.name + ".v"
v1bname = OPTS.openram_temp + v1bfile
s.verilog_write(v1bname)
# let's diff the result with a golden model
multi_golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)), vfile)
self.assertTrue(self.isdiff(vname, multi_golden))
one_golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)), v1bfile)
self.assertTrue(self.isdiff(v1bname, one_golden))
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(testRunner=debugTestRunner())

View File

@ -72,6 +72,7 @@ BROKEN_STAMPS = \
sky130/23_lib_sram_prune_test.ok \
sky131/23_lib_sram_test.ok \
%/26_hspice_pex_pinv_test.ok \
%/27_verilog_multibank_test.ok \
%/50_riscv_1k_1rw1r_func_test.ok \
%/50_riscv_1k_1rw_func_test.ok \
%/50_riscv_1rw1r_func_test.ok \

View File

@ -0,0 +1,73 @@
// OpenRAM SRAM model
// Words: 16
// Word size: 2
module sram_2_16_2_freepdk45(
`ifdef USE_POWER_PINS
vdd,
gnd,
`endif
// Port 0: RW
clk0,csb0,web0,addr0,din0,dout0
);
parameter DATA_WIDTH = 2 ;
parameter ADDR_WIDTH = 3 ;
parameter RAM_DEPTH = 1 << ADDR_WIDTH;
// FIXME: This delay is arbitrary.
parameter DELAY = 3 ;
parameter VERBOSE = 1 ; //Set to 0 to only display warnings
parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary
`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
input clk0; // clock
input csb0; // active low chip select
input web0; // active low write control
input [ADDR_WIDTH-1:0] addr0;
input [DATA_WIDTH-1:0] din0;
output [DATA_WIDTH-1:0] dout0;
reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];
reg csb0_reg;
reg web0_reg;
reg [ADDR_WIDTH-1:0] addr0_reg;
reg [DATA_WIDTH-1:0] din0_reg;
reg [DATA_WIDTH-1:0] dout0;
// All inputs are registers
always @(posedge clk0)
begin
csb0_reg = csb0;
web0_reg = web0;
addr0_reg = addr0;
din0_reg = din0;
#(T_HOLD) dout0 = 2'bx;
if ( !csb0_reg && web0_reg && VERBOSE )
$display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]);
if ( !csb0_reg && !web0_reg && VERBOSE )
$display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg);
end
// Memory Write Block Port 0
// Write Operation : When web0 = 0, csb0 = 0
always @ (negedge clk0)
begin : MEM_WRITE0
if ( !csb0_reg && !web0_reg ) begin
mem[addr0_reg][1:0] = din0_reg[1:0];
end
end
// Memory Read Block Port 0
// Read Operation : When web0 = 1, csb0 = 0
always @ (negedge clk0)
begin : MEM_READ0
if (!csb0_reg && web0_reg)
dout0 <= #(DELAY) mem[addr0_reg];
end
endmodule

View File

@ -0,0 +1,105 @@
module sram_2_16_2_freepdk45_top (
`ifdef USE_POWER_PINS
vdd,
gnd,
`endif
clk0,
addr0,
din0,
csb0,
web0,
dout0
);
parameter DATA_WIDTH = 2;
parameter ADDR_WIDTH= 4;
parameter BANK_SEL = 1;
parameter NUM_WMASK = 0;
`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
input clk0;
input [ADDR_WIDTH - 1 : 0] addr0;
input [DATA_WIDTH - 1: 0] din0;
input csb0;
input web0;
output reg [DATA_WIDTH - 1 : 0] dout0;
reg [BANK_SEL - 1 : 0] addr0_reg;
wire [DATA_WIDTH - 1 : 0] dout0_bank0;
reg web0_bank0;
reg csb0_bank0;
wire [DATA_WIDTH - 1 : 0] dout0_bank1;
reg web0_bank1;
reg csb0_bank1;
sram_2_16_2_freepdk45 bank0 (
`ifdef USE_POWER_PINS
.vdd(vdd),
.gnd(gnd),
`endif
.clk0(clk0),
.addr0(addr0[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din0(din0),
.csb0(csb0_bank0),
.web0(web0_bank0),
.dout0(dout0_bank0)
);
sram_2_16_2_freepdk45 bank1 (
`ifdef USE_POWER_PINS
.vdd(vdd),
.gnd(gnd),
`endif
.clk0(clk0),
.addr0(addr0[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din0(din0),
.csb0(csb0_bank1),
.web0(web0_bank1),
.dout0(dout0_bank1)
);
always @(posedge clk0) begin
addr0_reg <= addr0[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL];
end
always @(*) begin
case (addr0_reg)
0: begin
dout0 = dout0_bank0;
end
1: begin
dout0 = dout0_bank1;
end
endcase
end
always @(*) begin
csb0_bank0 = 1'b1;
web0_bank0 = 1'b1;
csb0_bank1 = 1'b1;
web0_bank1 = 1'b1;
case (addr0[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
0: begin
web0_bank0 = web0;
csb0_bank0 = csb0;
end
1: begin
web0_bank1 = web0;
csb0_bank1 = csb0;
end
endcase
end
endmodule

View File

@ -0,0 +1,73 @@
// OpenRAM SRAM model
// Words: 16
// Word size: 2
module sram_2_16_2_scn4m_subm(
`ifdef USE_POWER_PINS
vdd,
gnd,
`endif
// Port 0: RW
clk0,csb0,web0,addr0,din0,dout0
);
parameter DATA_WIDTH = 2 ;
parameter ADDR_WIDTH = 3 ;
parameter RAM_DEPTH = 1 << ADDR_WIDTH;
// FIXME: This delay is arbitrary.
parameter DELAY = 3 ;
parameter VERBOSE = 1 ; //Set to 0 to only display warnings
parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary
`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
input clk0; // clock
input csb0; // active low chip select
input web0; // active low write control
input [ADDR_WIDTH-1:0] addr0;
input [DATA_WIDTH-1:0] din0;
output [DATA_WIDTH-1:0] dout0;
reg [DATA_WIDTH-1:0] mem [0:RAM_DEPTH-1];
reg csb0_reg;
reg web0_reg;
reg [ADDR_WIDTH-1:0] addr0_reg;
reg [DATA_WIDTH-1:0] din0_reg;
reg [DATA_WIDTH-1:0] dout0;
// All inputs are registers
always @(posedge clk0)
begin
csb0_reg = csb0;
web0_reg = web0;
addr0_reg = addr0;
din0_reg = din0;
#(T_HOLD) dout0 = 2'bx;
if ( !csb0_reg && web0_reg && VERBOSE )
$display($time," Reading %m addr0=%b dout0=%b",addr0_reg,mem[addr0_reg]);
if ( !csb0_reg && !web0_reg && VERBOSE )
$display($time," Writing %m addr0=%b din0=%b",addr0_reg,din0_reg);
end
// Memory Write Block Port 0
// Write Operation : When web0 = 0, csb0 = 0
always @ (negedge clk0)
begin : MEM_WRITE0
if ( !csb0_reg && !web0_reg ) begin
mem[addr0_reg][1:0] = din0_reg[1:0];
end
end
// Memory Read Block Port 0
// Read Operation : When web0 = 1, csb0 = 0
always @ (negedge clk0)
begin : MEM_READ0
if (!csb0_reg && web0_reg)
dout0 <= #(DELAY) mem[addr0_reg];
end
endmodule

View File

@ -0,0 +1,105 @@
module sram_2_16_2_scn4m_subm_top (
`ifdef USE_POWER_PINS
vdd,
gnd,
`endif
clk0,
addr0,
din0,
csb0,
web0,
dout0
);
parameter DATA_WIDTH = 2;
parameter ADDR_WIDTH= 4;
parameter BANK_SEL = 1;
parameter NUM_WMASK = 0;
`ifdef USE_POWER_PINS
inout vdd;
inout gnd;
`endif
input clk0;
input [ADDR_WIDTH - 1 : 0] addr0;
input [DATA_WIDTH - 1: 0] din0;
input csb0;
input web0;
output reg [DATA_WIDTH - 1 : 0] dout0;
reg [BANK_SEL - 1 : 0] addr0_reg;
wire [DATA_WIDTH - 1 : 0] dout0_bank0;
reg web0_bank0;
reg csb0_bank0;
wire [DATA_WIDTH - 1 : 0] dout0_bank1;
reg web0_bank1;
reg csb0_bank1;
sram_2_16_2_scn4m_subm bank0 (
`ifdef USE_POWER_PINS
.vdd(vdd),
.gnd(gnd),
`endif
.clk0(clk0),
.addr0(addr0[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din0(din0),
.csb0(csb0_bank0),
.web0(web0_bank0),
.dout0(dout0_bank0)
);
sram_2_16_2_scn4m_subm bank1 (
`ifdef USE_POWER_PINS
.vdd(vdd),
.gnd(gnd),
`endif
.clk0(clk0),
.addr0(addr0[ADDR_WIDTH - BANK_SEL - 1 : 0]),
.din0(din0),
.csb0(csb0_bank1),
.web0(web0_bank1),
.dout0(dout0_bank1)
);
always @(posedge clk0) begin
addr0_reg <= addr0[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL];
end
always @(*) begin
case (addr0_reg)
0: begin
dout0 = dout0_bank0;
end
1: begin
dout0 = dout0_bank1;
end
endcase
end
always @(*) begin
csb0_bank0 = 1'b1;
web0_bank0 = 1'b1;
csb0_bank1 = 1'b1;
web0_bank1 = 1'b1;
case (addr0[ADDR_WIDTH - 1 : ADDR_WIDTH - BANK_SEL])
0: begin
web0_bank0 = web0;
csb0_bank0 = csb0;
end
1: begin
web0_bank1 = web0;
csb0_bank1 = csb0;
end
endcase
end
endmodule

View File

@ -26,7 +26,6 @@ import shutil
import debug
from globals import OPTS
from .run_script import *
# Keep track of statistics
num_drc_runs = 0
num_lvs_runs = 0
@ -96,13 +95,24 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write("gds warning default\n")
# Flatten the transistors
# Bug in Netgen 1.5.194 when using this...
f.write("gds flatglob *_?mos_m*\n")
try:
from tech import blackbox_cells
except ImportError:
blackbox_cells = []
try:
from tech import flatglob
except ImportError:
flatglob = []
f.write("gds readonly true\n")
for entry in flatglob:
f.write("gds flatglob " +entry + "\n")
# These two options are temporarily disabled until Tim fixes a bug in magic related
# to flattening channel routes and vias (hierarchy with no devices in it). Otherwise,
# they appear to be disconnected.
f.write("gds flatten true\n")
f.write("gds ordering true\n")
f.write("gds readonly true\n")
f.write("gds read {}\n".format(gds_name))
f.write('puts "Finished reading gds {}"\n'.format(gds_name))
f.write("load {}\n".format(cell_name))
@ -164,10 +174,6 @@ def write_drc_script(cell_name, gds_name, extract, final_verification, output_pa
f.write("#!/bin/sh\n")
f.write('export OPENRAM_TECH="{}"\n'.format(os.environ['OPENRAM_TECH']))
# Copy the bitcell mag files if they exist
try:
from tech import blackbox_cells
except ImportError:
blackbox_cells = []
for blackbox_cell_name in blackbox_cells:
mag_file = OPTS.openram_tech + "maglef_lib/" + blackbox_cell_name + ".mag"
debug.check(os.path.isfile(mag_file), "Could not find blackbox cell {}".format(mag_file))

View File

@ -11,6 +11,7 @@ RUN apt-get --no-install-recommends -y upgrade
# General tools for building etc.
RUN apt-get install --no-install-recommends -y build-essential git ssh vim gosu autoconf automake libtool bison flex
# Use bash instead of dash
# Must be on one line or else ln won't work without a shell!
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
# Needed by OpenRAM
RUN apt-get install --no-install-recommends -y python3 python3-numpy python3-scipy python3-pip python3-matplotlib python3-venv python3-sklearn python3-subunit python3-coverage
@ -30,8 +31,8 @@ WORKDIR /root
RUN git clone https://github.com/KLayout/klayout
WORKDIR /root/klayout
RUN git checkout ${KLAYOUT_COMMIT}
RUN ./build.sh -qt5 -debug -j 8 \
&& cp -r bin-debug /usr/local/klayout
RUN ./build.sh -qt5 -debug -j$(nproc)
RUN cp -r bin-debug /usr/local/klayout
RUN rm -rf /root/klayout
### Trilinos ###
@ -99,15 +100,15 @@ RUN ../configure CXXFLAGS="-O3 -std=c++11" \
RUN make -j 4 install
### Ngspice ###
ARG NGSPICE_COMIT=032b1c32c4dbad45ff132bcfac1dbecadbd8abb0
ARG NGSPICE_COMMIT=032b1c32c4dbad45ff132bcfac1dbecadbd8abb0
WORKDIR /root
RUN git clone git://git.code.sf.net/p/ngspice/ngspice
WORKDIR /root/ngspice
RUN git checkout ${NGSPICE_COMMIT}
RUN ./autogen.sh \
&& ./configure --enable-openmp --with-readline \
&& make \
&& make install
RUN ./autogen.sh
RUN ./configure --enable-openmp --with-readline
RUN make
RUN make install
RUN rm -rf /root/ngspice
### Netgen ###
@ -118,9 +119,9 @@ WORKDIR /root
RUN git clone git://opencircuitdesign.com/netgen netgen
WORKDIR /root/netgen
RUN git checkout ${NETGEN_COMMIT}
RUN ./configure \
&& make -j$(nproc) \
&& make install
RUN ./configure
RUN make -j$(nproc)
RUN make install
RUN rm -rf /root/netgen
### iVerilog ###
@ -137,9 +138,9 @@ WORKDIR /root/magic
RUN git checkout ${MAGIC_COMMIT}
COPY mrg.patch /root/magic
RUN git apply mrg.patch
RUN ./configure \
&& make \
&& make install
RUN ./configure
RUN make
RUN make install
RUN rm -rf /root/magic

View File

@ -90,8 +90,8 @@ class sky130_replica_column(sky130_bitcell_base_array):
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
self.add_pin("top_gate", "INPUT")
self.add_pin("bot_gate", "INPUT")
#self.add_pin("top_gate", "INPUT")
#self.add_pin("bot_gate", "INPUT")
def add_modules(self):
self.replica_cell = factory.create(module_type="replica_bitcell_1port", version="opt1")
@ -220,6 +220,16 @@ class sky130_replica_column(sky130_bitcell_base_array):
width=self.width,
height=wl_pin.height())
# for colend in [self.cell_inst[0], self.cell_inst[self.row_size]]:
# inst = self.cell_inst[row]
# for pin_name in ["top_gate", "bot_gate"]:
# pin = inst.get_pin("gate")
# self.add_layout_pin(text=pin_name,
# layer=pin.layer,
# offset=pin.ll(),
# width=pin.width(),
# height=pin.height())
for row in range(self.row_size + 2):
inst = self.cell_inst[row]
# add only 1 label per col
@ -235,7 +245,6 @@ class sky130_replica_column(sky130_bitcell_base_array):
self.objs.append(geometry.label("vdd", layer["nwell"], pin.center()))
if 'VNB' or 'vnb' in self.cell_inst[row].mod.pins:
print("welling")
try:
from tech import layer_override
if layer_override['VNB']:

View File

@ -781,6 +781,16 @@ library_prefix_name = "sky130_fd_bd_sram__"
# List of cells to skip running DRC/LVS on directly
# This will look for a maglef file and copy it over the mag file
# before DRC after extraction
flatglob = ["*_?mos_m*",
"sky130_fd_bd_sram__sram_sp_cell_fom_serifs",
"sky130_fd_bd_sram__openram_sp_cell_opt1a_cell",
"sky130_fd_bd_sram__openram_sp_cell_opt1a_replica_ce",
"sky130_fd_bd_sram__openram_sp_cell_opt1_replica_cell",
"sky130_fd_bd_sram__openram_sp_cell_opt1_replica_ce",
"sky130_fd_bd_sram__openram_sp_cell_opt1a_cell",
"sky130_fd_bd_sram__sram_sp_cell_fom_serifs"]
blackbox_cells = ["sky130_fd_bd_sram__openram_dp_cell",
"sky130_fd_bd_sram__openram_dp_cell_dummy",
"sky130_fd_bd_sram__openram_dp_cell_replica",