Merge branch 'dev'

This commit is contained in:
Matt Guthaus 2019-09-27 09:50:50 -07:00
commit 062156fbd3
43 changed files with 4730 additions and 334 deletions

View File

@ -164,18 +164,8 @@ If you want to support a enw technology, you will need to create:
+ a setup script for each technology you want to use + a setup script for each technology you want to use
+ a technology directory for each technology with the base cells + a technology directory for each technology with the base cells
All setup scripts should be in the setup\_scripts directory under the We provide two technology examples for [SCMOS] and [FreePDK45]. Each
$OPENRAM\_TECH directory. We provide two technology examples for specific technology (e.g., [FreePDK45]) should be a subdirectory
[SCMOS] and [FreePDK45]. Please look at the following file for an
example of what is needed for OpenRAM:
```
$OPENRAM_TECH/setup_scripts/setup_openram_freepdk45.py
```
Each setup script should be named as: setup\_openram\_{tech name}.py.
Each specific technology (e.g., [FreePDK45]) should be a subdirectory
(e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files: (e.g., $OPENRAM_TECH/freepdk45) and include certain folders and files:
* gds_lib folder with all the .gds (premade) library cells: * gds_lib folder with all the .gds (premade) library cells:
* dff.gds * dff.gds
@ -183,6 +173,7 @@ Each specific technology (e.g., [FreePDK45]) should be a subdirectory
* write_driver.gds * write_driver.gds
* cell_6t.gds * cell_6t.gds
* replica\_cell\_6t.gds * replica\_cell\_6t.gds
* dummy\_cell\_6t.gds
* sp_lib folder with all the .sp (premade) library netlists for the above cells. * sp_lib folder with all the .sp (premade) library netlists for the above cells.
* layers.map * layers.map
* A valid tech Python module (tech directory with \_\_init\_\_.py and tech.py) with: * A valid tech Python module (tech directory with \_\_init\_\_.py and tech.py) with:

View File

@ -64,8 +64,8 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
self.sp_write(tempspice) self.sp_write(tempspice)
self.gds_write(tempgds) self.gds_write(tempgds)
num_drc_errors = verify.run_drc(self.name, tempgds, final_verification) num_drc_errors = verify.run_drc(self.name, tempgds, extract=True, final_verification=final_verification)
num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification) num_lvs_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors)) debug.check(num_drc_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_drc_errors))
debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors)) debug.check(num_lvs_errors == 0,"LVS failed for {0} with {1} errors(s)".format(self.name,num_lvs_errors))
total_drc_errors += num_drc_errors total_drc_errors += num_drc_errors
@ -83,7 +83,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
global total_drc_errors global total_drc_errors
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.gds_write(tempgds) self.gds_write(tempgds)
num_errors = verify.run_drc(self.name, tempgds, final_verification) num_errors = verify.run_drc(self.name, tempgds, final_verification=final_verification)
total_drc_errors += num_errors total_drc_errors += num_errors
debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error)) debug.check(num_errors == 0,"DRC failed for {0} with {1} error(s)".format(self.name,num_error))
@ -100,7 +100,7 @@ class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name) tempgds = "{0}/{1}.gds".format(OPTS.openram_temp,self.name)
self.sp_write(tempspice) self.sp_write(tempspice)
self.gds_write(tempgds) self.gds_write(tempgds)
num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification) num_errors = verify.run_lvs(self.name, tempgds, tempspice, final_verification=final_verification)
total_lvs_errors += num_errors total_lvs_errors += num_errors
debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors)) debug.check(num_errors == 0,"LVS failed for {0} with {1} error(s)".format(self.name,num_errors))
os.remove(tempspice) os.remove(tempspice)

View File

@ -6,6 +6,7 @@
# All rights reserved. # All rights reserved.
# #
import itertools import itertools
import collections
import geometry import geometry
import gdsMill import gdsMill
import debug import debug
@ -839,10 +840,10 @@ class layout():
#hcg = {} #hcg = {}
# Initialize the vertical conflict graph (vcg) and make a list of all pins # Initialize the vertical conflict graph (vcg) and make a list of all pins
vcg = {} vcg = collections.OrderedDict()
# Create names for the nets for the graphs # Create names for the nets for the graphs
nets = {} nets = collections.OrderedDict()
index = 0 index = 0
#print(netlist) #print(netlist)
for pin_list in netlist: for pin_list in netlist:

View File

@ -319,13 +319,13 @@ class spice():
corner_slew = SLEW_APPROXIMATION*corner_delay corner_slew = SLEW_APPROXIMATION*corner_delay
return delay_data(corner_delay, corner_slew) return delay_data(corner_delay, corner_slew)
def get_stage_effort(self, corner, slew, load=0.0): def get_stage_effort(self, cout, inp_is_rise=True):
"""Inform users undefined delay module while building new modules""" """Inform users undefined delay module while building new modules"""
debug.warning("Design Class {0} logical effort function needs to be defined" debug.warning("Design Class {0} logical effort function needs to be defined"
.format(self.__class__.__name__)) .format(self.__class__.__name__))
debug.warning("Class {0} name {1}" debug.warning("Class {0} name {1}"
.format(self.__class__.__name__, .format(self.__class__.__name__,
self.name)) self.name))
return None return None
def get_cin(self): def get_cin(self):
@ -392,7 +392,7 @@ class spice():
"""Returns delay increase due to voltage. """Returns delay increase due to voltage.
Implemented as linear factor based off nominal voltage. Implemented as linear factor based off nominal voltage.
""" """
return tech.spice['vdd_nominal']/voltage return tech.spice["nom_supply_voltage"]/voltage
def get_temp_delay_factor(self, temp): def get_temp_delay_factor(self, temp):
"""Returns delay increase due to temperature (in C). """Returns delay increase due to temperature (in C).
@ -400,11 +400,11 @@ class spice():
""" """
#Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV #Some portions of equation condensed (phi_t = k*T/q for T in Kelvin) in mV
#(k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V #(k/q)/100 = .008625, The division 100 simplifies the conversion from C to K and mV to V
thermal_voltage_nom = .008625*tech.spice["temp_nominal"] thermal_voltage_nom = 0.008625*tech.spice["nom_temperature"]
thermal_voltage = .008625*temp thermal_voltage = 0.008625*temp
vthresh = (tech.spice["v_threshold_typical"]+2*(thermal_voltage-thermal_voltage_nom)) vthresh = (tech.spice["nom_threshold"]+2*(thermal_voltage-thermal_voltage_nom))
#Calculate effect on Vdd-Vth. The current vdd is not used here. A separate vdd factor is calculated. #Calculate effect on Vdd-Vth. The current vdd is not used here. A separate vdd factor is calculated.
return (tech.spice['vdd_nominal'] - tech.spice["v_threshold_typical"])/(tech.spice['vdd_nominal']-vthresh) return (tech.spice["nom_supply_voltage"] - tech.spice["nom_threshold"])/(tech.spice["nom_supply_voltage"]-vthresh)
def return_delay(self, delay, slew): def return_delay(self, delay, slew):
return delay_data(delay, slew) return delay_data(delay, slew)

View File

@ -259,7 +259,7 @@ class delay(simulation):
"""Sets important names for characterization such as Sense amp enable and internal bit nets.""" """Sets important names for characterization such as Sense amp enable and internal bit nets."""
port = self.read_ports[0] port = self.read_ports[0]
self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, self.probe_data)) '{}{}_{}'.format(self.dout_name, port, self.probe_data))
self.sen_name = self.get_sen_name(self.graph.all_paths) self.sen_name = self.get_sen_name(self.graph.all_paths)
@ -1050,7 +1050,7 @@ class delay(simulation):
def get_address_row_number(self, probe_address): def get_address_row_number(self, probe_address):
"""Calculates wordline row number of data bit under test using address and column mux size""" """Calculates wordline row number of data bit under test using address and column mux size"""
return int(probe_address[self.sram.col_addr_size:],2) return int(probe_address[self.sram.col_addr_size:],2)
def prepare_netlist(self): def prepare_netlist(self):
@ -1285,13 +1285,13 @@ class delay(simulation):
debug.warning("Analytical characterization results are not supported for multiport.") debug.warning("Analytical characterization results are not supported for multiport.")
# Probe set to 0th bit, does not matter for analytical delay. # Probe set to 0th bit, does not matter for analytical delay.
self.set_probe('0', 0) self.set_probe('0'*self.addr_size, 0)
self.create_graph() self.create_graph()
self.set_internal_spice_names() self.set_internal_spice_names()
self.create_measurement_names() self.create_measurement_names()
port = self.read_ports[0] port = self.read_ports[0]
self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, self.probe_data)) '{}{}_{}'.format(self.dout_name, port, self.probe_data))
# Select the path with the bitline (bl) # Select the path with the bitline (bl)

View File

@ -349,7 +349,7 @@ class functional(simulation):
# Generate CLK signals # Generate CLK signals
for port in self.all_ports: for port in self.all_ports:
self.stim.gen_pulse(sig_name="{0}{1}".format(tech.spice["clk"], port), self.stim.gen_pulse(sig_name="{0}{1}".format("clk", port),
v1=self.gnd_voltage, v1=self.gnd_voltage,
v2=self.vdd_voltage, v2=self.vdd_voltage,
offset=self.period, offset=self.period,
@ -402,7 +402,7 @@ class functional(simulation):
# For now, only testing these using first read port. # For now, only testing these using first read port.
port = self.read_ports[0] port = self.read_ports[0]
self.graph.get_all_paths('{}{}'.format(tech.spice["clk"], port), self.graph.get_all_paths('{}{}'.format("clk", port),
'{}{}_{}'.format(self.dout_name, port, 0).lower()) '{}{}_{}'.format(self.dout_name, port, 0).lower())
self.sen_name = self.get_sen_name(self.graph.all_paths) self.sen_name = self.get_sen_name(self.graph.all_paths)

View File

@ -109,6 +109,8 @@ class lib:
#set the read and write port as inputs. #set the read and write port as inputs.
self.write_data_bus(port) self.write_data_bus(port)
self.write_addr_bus(port) self.write_addr_bus(port)
if self.sram.write_size:
self.write_wmask_bus(port)
self.write_control_pins(port) #need to split this into sram and port control signals self.write_control_pins(port) #need to split this into sram and port control signals
self.write_clk_timing_power(port) self.write_clk_timing_power(port)
@ -297,6 +299,15 @@ class lib:
self.lib.write(" bit_to : {0};\n".format(self.sram.addr_size - 1)) self.lib.write(" bit_to : {0};\n".format(self.sram.addr_size - 1))
self.lib.write(" }\n\n") self.lib.write(" }\n\n")
if self.sram.write_size:
self.lib.write(" type (wmask){\n")
self.lib.write(" base_type : array;\n")
self.lib.write(" data_type : bit;\n")
self.lib.write(" bit_width : {0};\n".format(self.sram.num_wmasks))
self.lib.write(" bit_from : 0;\n")
self.lib.write(" bit_to : {0};\n".format(self.sram.num_wmasks - 1))
self.lib.write(" }\n\n")
def write_FF_setuphold(self, port): def write_FF_setuphold(self, port):
""" Adds Setup and Hold timing results""" """ Adds Setup and Hold timing results"""
@ -400,6 +411,20 @@ class lib:
self.lib.write(" }\n") self.lib.write(" }\n")
self.lib.write(" }\n\n") self.lib.write(" }\n\n")
def write_wmask_bus(self, port):
""" Adds addr bus timing results."""
self.lib.write(" bus(wmask{0}){{\n".format(port))
self.lib.write(" bus_type : wmask; \n")
self.lib.write(" direction : input; \n")
self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"] / 1000))
self.lib.write(" max_transition : {0};\n".format(self.slews[-1]))
self.lib.write(" pin(wmask{0}[{1}:0])".format(port, self.sram.num_wmasks - 1))
self.lib.write("{\n")
self.write_FF_setuphold(port)
self.lib.write(" }\n")
self.lib.write(" }\n\n")
def write_control_pins(self, port): def write_control_pins(self, port):
""" Adds control pins timing results.""" """ Adds control pins timing results."""

View File

@ -336,10 +336,10 @@ class setup_hold():
for self.related_input_slew in related_slews: for self.related_input_slew in related_slews:
for self.constrained_input_slew in constrained_slews: for self.constrained_input_slew in constrained_slews:
# convert from ps to ns # convert from ps to ns
LH_setup.append(tech.spice["msflop_setup"]/1e3) LH_setup.append(tech.spice["dff_setup"]/1e3)
HL_setup.append(tech.spice["msflop_setup"]/1e3) HL_setup.append(tech.spice["dff_setup"]/1e3)
LH_hold.append(tech.spice["msflop_hold"]/1e3) LH_hold.append(tech.spice["dff_hold"]/1e3)
HL_hold.append(tech.spice["msflop_hold"]/1e3) HL_hold.append(tech.spice["dff_hold"]/1e3)
times = {"setup_times_LH": LH_setup, times = {"setup_times_LH": LH_setup,
"setup_times_HL": HL_setup, "setup_times_HL": HL_setup,

View File

@ -46,10 +46,10 @@ class simulation():
""" sets feasible timing parameters """ """ sets feasible timing parameters """
self.period = tech.spice["feasible_period"] self.period = tech.spice["feasible_period"]
self.slew = tech.spice["rise_time"]*2 self.slew = tech.spice["rise_time"]*2
self.load = tech.spice["msflop_in_cap"]*4 self.load = tech.spice["dff_in_cap"]*4
self.v_high = self.vdd_voltage - tech.spice["v_threshold_typical"] self.v_high = self.vdd_voltage - tech.spice["nom_threshold"]
self.v_low = tech.spice["v_threshold_typical"] self.v_low = tech.spice["nom_threshold"]
self.gnd_voltage = 0 self.gnd_voltage = 0
def create_signal_names(self): def create_signal_names(self):
@ -301,7 +301,7 @@ class simulation():
pin_names.append("WEB{0}".format(port)) pin_names.append("WEB{0}".format(port))
for port in range(total_ports): for port in range(total_ports):
pin_names.append("{0}{1}".format(tech.spice["clk"], port)) pin_names.append("{0}{1}".format("clk", port))
if self.write_size: if self.write_size:
for port in write_index: for port in write_index:
@ -312,7 +312,7 @@ class simulation():
for i in range(dbits): for i in range(dbits):
pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i)) pin_names.append("{0}{1}_{2}".format(dout_name,read_output, i))
pin_names.append("{0}".format(tech.spice["vdd_name"])) pin_names.append("{0}".format("vdd"))
pin_names.append("{0}".format(tech.spice["gnd_name"])) pin_names.append("{0}".format("gnd"))
return pin_names return pin_names

View File

@ -24,12 +24,12 @@ class stimuli():
""" Class for providing stimuli functions """ """ Class for providing stimuli functions """
def __init__(self, stim_file, corner): def __init__(self, stim_file, corner):
self.vdd_name = tech.spice["vdd_name"] self.vdd_name = "vdd"
self.gnd_name = tech.spice["gnd_name"] self.gnd_name = "gnd"
self.pmos_name = tech.spice["pmos"] self.pmos_name = tech.spice["pmos"]
self.nmos_name = tech.spice["nmos"] self.nmos_name = tech.spice["nmos"]
self.tx_width = tech.spice["minwidth_tx"] self.tx_width = tech.drc["minwidth_tx"]
self.tx_length = tech.spice["channel"] self.tx_length = tech.drc["minlength_channel"]
self.sf = stim_file self.sf = stim_file

View File

@ -507,16 +507,16 @@ def report_status():
debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).") debug.print_raw("Netlist only mode (no physical design is being done, netlist_only=False to disable).")
if not OPTS.route_supplies: if not OPTS.route_supplies:
debug.print_raw("Design supply routing skipped for run-time (incomplete GDS will not be saved) (route_supplies=True to enable).") debug.print_raw("Design supply routing skipped. Supplies will have multiple must-connect pins. (route_supplies=True to enable supply routing).")
if not OPTS.inline_lvsdrc: if not OPTS.inline_lvsdrc:
debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to enable).") debug.print_raw("DRC/LVS/PEX is only run on the top-level design to save run-time (inline_lvsdrc=True to do inline checking).")
if not OPTS.check_lvsdrc: if not OPTS.check_lvsdrc:
debug.print_raw("DRC/LVS/PEX is disabled (check_lvsdrc=True to enable).") debug.print_raw("DRC/LVS/PEX is disabled (check_lvsdrc=True to enable).")
if OPTS.analytical_delay: if OPTS.analytical_delay:
debug.print_raw("Characterization is disabled (using analytical delay models) (analytical_delay=False to enable).") debug.print_raw("Characterization is disabled (using analytical delay models) (analytical_delay=False to simulate).")
else: else:
if OPTS.spice_name!="": if OPTS.spice_name!="":
debug.print_raw("Performing simulation-based characterization with {}".format(OPTS.spice_name)) debug.print_raw("Performing simulation-based characterization with {}".format(OPTS.spice_name))

View File

@ -74,7 +74,9 @@ class bank(design.design):
# Remember the bank center for further placement # Remember the bank center for further placement
self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1) self.bank_array_ll = self.offset_all_coordinates().scale(-1,-1)
self.bank_array_ur = self.bitcell_array_inst.ur() self.bank_array_ur = self.bitcell_array_inst.ur()
self.bank_array_ul = self.bitcell_array_inst.ul()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
@ -928,7 +930,10 @@ class bank(design.design):
connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc())) connection.append((self.prefix+"wl_en{}".format(port), self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
if port in self.write_ports: if port in self.write_ports:
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc())) if port % 2:
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").rc()))
else:
connection.append((self.prefix+"w_en{}".format(port), self.port_data_inst[port].get_pin("w_en").lc()))
if port in self.read_ports: if port in self.read_ports:
connection.append((self.prefix+"s_en{}".format(port), self.port_data_inst[port].get_pin("s_en").lc())) connection.append((self.prefix+"s_en{}".format(port), self.port_data_inst[port].get_pin("s_en").lc()))

View File

@ -157,7 +157,7 @@ class bitcell_array(design.design):
bl_wire = self.gen_bl_wire() bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap() cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_rate"] freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
#Calculate the bitcell power which currently only includes leakage #Calculate the bitcell power which currently only includes leakage

View File

@ -33,9 +33,9 @@ class dff(design.design):
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load) c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"] freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["msflop_leakage"] power_leak = spice["dff_leakage"]
total_power = self.return_power(power_dyn, power_leak) total_power = self.return_power(power_dyn, power_leak)
return total_power return total_power
@ -44,8 +44,8 @@ class dff(design.design):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
from tech import parameter from tech import parameter
c_load = load c_load = load
c_para = spice["flop_para_cap"]#ff c_para = spice["dff_out_cap"]#ff
transition_prob = spice["flop_transition_prob"] transition_prob = 0.5
return transition_prob*(c_load + c_para) return transition_prob*(c_load + c_para)
def get_clk_cin(self): def get_clk_cin(self):
@ -57,4 +57,4 @@ class dff(design.design):
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function.""" """Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets) self.add_graph_edges(graph, port_nets)

View File

@ -22,11 +22,11 @@ class port_data(design.design):
sram_config.set_local_config(self) sram_config.set_local_config(self)
self.port = port self.port = port
if self.write_size: if self.write_size is not None:
self.num_wmasks = int(self.word_size/self.write_size) self.num_wmasks = int(self.word_size/self.write_size)
else: else:
self.num_wmasks = 0 self.num_wmasks = 0
if name == "": if name == "":
name = "port_data_{0}".format(self.port) name = "port_data_{0}".format(self.port)
design.design.__init__(self, name) design.design.__init__(self, name)
@ -58,7 +58,7 @@ class port_data(design.design):
if self.write_driver_array: if self.write_driver_array:
self.create_write_driver_array() self.create_write_driver_array()
if self.write_size: if self.write_size is not None:
self.create_write_mask_and_array() self.create_write_mask_and_array()
else: else:
self.write_mask_and_array_inst = None self.write_mask_and_array_inst = None
@ -70,9 +70,9 @@ class port_data(design.design):
self.create_column_mux_array() self.create_column_mux_array()
else: else:
self.column_mux_array_inst = None self.column_mux_array_inst = None
def create_layout(self): def create_layout(self):
self.compute_instance_offsets() self.compute_instance_offsets()
self.place_instances() self.place_instances()
@ -81,7 +81,7 @@ class port_data(design.design):
def add_pins(self): def add_pins(self):
""" Adding pins for port address module""" """ Adding pins for port address module"""
self.add_pin("rbl_bl","INOUT") self.add_pin("rbl_bl","INOUT")
self.add_pin("rbl_br","INOUT") self.add_pin("rbl_br","INOUT")
for bit in range(self.num_cols): for bit in range(self.num_cols):
@ -107,7 +107,7 @@ class port_data(design.design):
self.add_pin("vdd","POWER") self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd","GROUND")
def route_layout(self): def route_layout(self):
""" Create routing among the modules """ """ Create routing among the modules """
self.route_data_lines() self.route_data_lines()
@ -118,15 +118,15 @@ class port_data(design.design):
""" Add the pins """ """ Add the pins """
self.route_bitline_pins() self.route_bitline_pins()
self.route_control_pins() self.route_control_pins()
def route_data_lines(self): def route_data_lines(self):
""" Route the bitlines depending on the port type rw, w, or r. """ """ Route the bitlines depending on the port type rw, w, or r. """
if self.port in self.readwrite_ports: if self.port in self.readwrite_ports:
# (write_mask_and ->) write_driver -> sense_amp -> (column_mux ->) precharge -> bitcell_array # (write_mask_and ->) write_driver -> sense_amp -> (column_mux ->) precharge -> bitcell_array
self.route_write_mask_and_array_in(self.port) self.route_write_mask_and_array_in(self.port)
self.route_write_mask_and_array_to_write_driver(self.port) self.route_write_mask_and_array_to_write_driver(self.port)
self.route_write_driver_in(self.port) self.route_write_driver_in(self.port)
self.route_sense_amp_out(self.port) self.route_sense_amp_out(self.port)
self.route_write_driver_to_sense_amp(self.port) self.route_write_driver_to_sense_amp(self.port)
self.route_sense_amp_to_column_mux_or_precharge_array(self.port) self.route_sense_amp_to_column_mux_or_precharge_array(self.port)
@ -142,15 +142,15 @@ class port_data(design.design):
self.route_write_mask_and_array_to_write_driver(self.port) self.route_write_mask_and_array_to_write_driver(self.port)
self.route_write_driver_in(self.port) self.route_write_driver_in(self.port)
self.route_write_driver_to_column_mux_or_precharge_array(self.port) self.route_write_driver_to_column_mux_or_precharge_array(self.port)
self.route_column_mux_to_precharge_array(self.port) self.route_column_mux_to_precharge_array(self.port)
def route_supplies(self): def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd") self.copy_power_pins(inst,"gnd")
def add_modules(self): def add_modules(self):
# Extra column +1 is for RBL # Extra column +1 is for RBL
@ -163,23 +163,23 @@ class port_data(design.design):
if self.port in self.read_ports: if self.port in self.read_ports:
self.sense_amp_array = factory.create(module_type="sense_amp_array", self.sense_amp_array = factory.create(module_type="sense_amp_array",
word_size=self.word_size, word_size=self.word_size,
words_per_row=self.words_per_row) words_per_row=self.words_per_row)
self.add_mod(self.sense_amp_array) self.add_mod(self.sense_amp_array)
else: else:
self.sense_amp_array = None self.sense_amp_array = None
if self.col_addr_size > 0: if self.col_addr_size > 0:
self.column_mux_array = factory.create(module_type="column_mux_array", self.column_mux_array = factory.create(module_type="column_mux_array",
columns=self.num_cols, columns=self.num_cols,
word_size=self.word_size, word_size=self.word_size,
bitcell_bl=self.bl_names[self.port], bitcell_bl=self.bl_names[self.port],
bitcell_br=self.br_names[self.port]) bitcell_br=self.br_names[self.port])
self.add_mod(self.column_mux_array) self.add_mod(self.column_mux_array)
else: else:
self.column_mux_array = None self.column_mux_array = None
if self.port in self.write_ports: if self.port in self.write_ports:
self.write_driver_array = factory.create(module_type="write_driver_array", self.write_driver_array = factory.create(module_type="write_driver_array",
@ -187,11 +187,12 @@ class port_data(design.design):
word_size=self.word_size, word_size=self.word_size,
write_size=self.write_size) write_size=self.write_size)
self.add_mod(self.write_driver_array) self.add_mod(self.write_driver_array)
if self.write_size: if self.write_size is not None:
self.write_mask_and_array = factory.create(module_type="write_mask_and_array", self.write_mask_and_array = factory.create(module_type="write_mask_and_array",
columns=self.num_cols, columns=self.num_cols,
word_size=self.word_size, word_size=self.word_size,
write_size=self.write_size) write_size=self.write_size,
port = self.port)
self.add_mod(self.write_mask_and_array) self.add_mod(self.write_mask_and_array)
else: else:
self.write_mask_and_array = None self.write_mask_and_array = None
@ -207,8 +208,8 @@ class port_data(design.design):
if self.col_addr_size>0: if self.col_addr_size>0:
self.num_col_addr_lines = 2**self.col_addr_size self.num_col_addr_lines = 2**self.col_addr_size
else: else:
self.num_col_addr_lines = 0 self.num_col_addr_lines = 0
# A space for wells or jogging m2 between modules # A space for wells or jogging m2 between modules
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
@ -216,7 +217,7 @@ class port_data(design.design):
# create arrays of bitline and bitline_bar names for read, write, or all ports # create arrays of bitline and bitline_bar names for read, write, or all ports
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
self.bl_names = self.bitcell.get_all_bl_names() self.bl_names = self.bitcell.get_all_bl_names()
self.br_names = self.bitcell.get_all_br_names() self.br_names = self.bitcell.get_all_br_names()
self.wl_names = self.bitcell.get_all_wl_names() self.wl_names = self.bitcell.get_all_wl_names()
@ -226,7 +227,7 @@ class port_data(design.design):
if not self.precharge_array: if not self.precharge_array:
self.precharge_array_inst = None self.precharge_array_inst = None
return return
self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port), self.precharge_array_inst = self.add_inst(name="precharge_array{}".format(self.port),
mod=self.precharge_array) mod=self.precharge_array)
@ -245,16 +246,16 @@ class port_data(design.design):
temp.extend(["p_en_bar", "vdd"]) temp.extend(["p_en_bar", "vdd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_precharge_array(self, offset): def place_precharge_array(self, offset):
""" Placing Precharge """ """ Placing Precharge """
self.precharge_array_inst.place(offset=offset, mirror="MX") self.precharge_array_inst.place(offset=offset, mirror="MX")
def create_column_mux_array(self): def create_column_mux_array(self):
""" Creating Column Mux when words_per_row > 1 . """ """ Creating Column Mux when words_per_row > 1 . """
self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port), self.column_mux_array_inst = self.add_inst(name="column_mux_array{}".format(self.port),
mod=self.column_mux_array) mod=self.column_mux_array)
@ -270,15 +271,15 @@ class port_data(design.design):
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_column_mux_array(self, offset): def place_column_mux_array(self, offset):
""" Placing Column Mux when words_per_row > 1 . """ """ Placing Column Mux when words_per_row > 1 . """
if self.col_addr_size == 0: if self.col_addr_size == 0:
return return
self.column_mux_array_inst.place(offset=offset, mirror="MX") self.column_mux_array_inst.place(offset=offset, mirror="MX")
def create_sense_amp_array(self): def create_sense_amp_array(self):
""" Creating Sense amp """ """ Creating Sense amp """
self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port), self.sense_amp_array_inst = self.add_inst(name="sense_amp_array{}".format(self.port),
@ -293,11 +294,11 @@ class port_data(design.design):
else: else:
temp.append(self.bl_names[self.port]+"_out_{0}".format(bit)) temp.append(self.bl_names[self.port]+"_out_{0}".format(bit))
temp.append(self.br_names[self.port]+"_out_{0}".format(bit)) temp.append(self.br_names[self.port]+"_out_{0}".format(bit))
temp.extend(["s_en", "vdd", "gnd"]) temp.extend(["s_en", "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_sense_amp_array(self, offset): def place_sense_amp_array(self, offset):
""" Placing Sense amp """ """ Placing Sense amp """
self.sense_amp_array_inst.place(offset=offset, mirror="MX") self.sense_amp_array_inst.place(offset=offset, mirror="MX")
@ -320,7 +321,7 @@ class port_data(design.design):
temp.append(self.bl_names[self.port] + "_out_{0}".format(bit)) temp.append(self.bl_names[self.port] + "_out_{0}".format(bit))
temp.append(self.br_names[self.port] + "_out_{0}".format(bit)) temp.append(self.br_names[self.port] + "_out_{0}".format(bit))
if self.write_size: if self.write_size is not None:
for i in range(self.num_wmasks): for i in range(self.num_wmasks):
temp.append("wdriver_sel_{}".format(i)) temp.append("wdriver_sel_{}".format(i))
else: else:

View File

@ -382,7 +382,7 @@ class replica_bitcell_array(design.design):
bl_wire = self.gen_bl_wire() bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap() cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_rate"] freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)
#Calculate the bitcell power which currently only includes leakage #Calculate the bitcell power which currently only includes leakage

View File

@ -20,7 +20,7 @@ class write_mask_and_array(design.design):
The write mask AND array goes between the write driver array and the sense amp array. The write mask AND array goes between the write driver array and the sense amp array.
""" """
def __init__(self, name, columns, word_size, write_size): def __init__(self, name, columns, word_size, write_size, port=0):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("columns: {0}".format(columns)) self.add_comment("columns: {0}".format(columns))
@ -30,6 +30,7 @@ class write_mask_and_array(design.design):
self.columns = columns self.columns = columns
self.word_size = word_size self.word_size = word_size
self.write_size = write_size self.write_size = write_size
self.port = port
self.words_per_row = int(columns / word_size) self.words_per_row = int(columns / word_size)
self.num_wmasks = int(word_size / write_size) self.num_wmasks = int(word_size / write_size)
@ -105,57 +106,48 @@ class write_mask_and_array(design.design):
def add_layout_pins(self): def add_layout_pins(self):
self.nand2 = factory.create(module_type="pnand2") self.nand2 = factory.create(module_type="pnand2")
supply_pin=self.nand2.get_pin("vdd") supply_pin=self.nand2.get_pin("vdd")
for i in range(self.num_wmasks):
wmask_in_pin = self.and2_insts[i].get_pin("A")
self.add_layout_pin(text="wmask_in_{0}".format(i),
layer=wmask_in_pin.layer,
offset=wmask_in_pin.ll(),
width=wmask_in_pin.width(),
height=wmask_in_pin.height())
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=wmask_in_pin.center())
# Create the enable pin that connects all write mask AND array's B pins
beg_en_pin = self.and2_insts[0].get_pin("B")
end_en_pin = self.and2_insts[self.num_wmasks-1].get_pin("B")
if self.port % 2:
# Extend metal3 to edge of AND array in multiport
en_to_edge = self.and2.width - beg_en_pin.cx()
self.add_layout_pin(text="en",
layer="metal3",
offset=beg_en_pin.bc(),
width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
else:
self.add_layout_pin(text="en",
layer="metal3",
offset=beg_en_pin.bc(),
width=end_en_pin.cx() - beg_en_pin.cx())
for i in range(self.num_wmasks):
# Copy remaining layout pins
self.copy_layout_pin(self.and2_insts[i],"A","wmask_in_{0}".format(i))
self.copy_layout_pin(self.and2_insts[i],"Z","wmask_out_{0}".format(i))
# Add via connections to metal3 for AND array's B pin
en_pin = self.and2_insts[i].get_pin("B") en_pin = self.and2_insts[i].get_pin("B")
# Add the M1->M2 stack
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=en_pin.center()) offset=en_pin.center())
# Add the M2->M3 stack
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=en_pin.center()) offset=en_pin.center())
# Route en pin between AND gates
if i < self.num_wmasks-1:
self.add_layout_pin(text="en",
layer="metal3",
offset=en_pin.bc(),
width = self.en_width(i),
height = drc('minwidth_metal3'))
wmask_out_pin = self.and2_insts[i].get_pin("Z")
self.add_layout_pin(text="wmask_out_{0}".format(i),
layer=wmask_out_pin.layer,
offset=wmask_out_pin.ll(),
width=wmask_out_pin.width(),
height=wmask_out_pin.height())
self.add_power_pin("gnd", vector(supply_pin.width() + i * self.wmask_en_len, 0)) self.add_power_pin("gnd", vector(supply_pin.width() + i * self.wmask_en_len, 0))
self.add_power_pin("vdd", vector(supply_pin.width() + i * self.wmask_en_len, self.height)) self.add_power_pin("vdd", vector(supply_pin.width() + i * self.wmask_en_len, self.height))
# Route power and ground rails together
if i < self.num_wmasks-1: if i < self.num_wmasks-1:
for n in ["gnd","vdd"]: for n in ["gnd","vdd"]:
pin = self.and2_insts[i].get_pin(n) pin = self.and2_insts[i].get_pin(n)
next_pin = self.and2_insts[i+1].get_pin(n) next_pin = self.and2_insts[i+1].get_pin(n)
self.add_path("metal1",[pin.center(),next_pin.center()]) self.add_path("metal1",[pin.center(),next_pin.center()])
def en_width(self, pin):
en_pin = self.and2_insts[pin].get_pin("B")
next_en_pin = self.and2_insts[pin+1].get_pin("B")
width = next_en_pin.center() - en_pin.center()
# Return x coordinates only
return width[0]
def get_cin(self): def get_cin(self):
"""Get the relative capacitance of all the input connections in the bank""" """Get the relative capacitance of all the input connections in the bank"""
# The enable is connected to an and2 for every row. # The enable is connected to an and2 for every row.

View File

@ -58,10 +58,7 @@ debug.print_raw("Words per row: {}".format(c.words_per_row))
output_extensions = ["sp","v","lib","py","html","log"] output_extensions = ["sp","v","lib","py","html","log"]
# Only output lef/gds if back-end # Only output lef/gds if back-end
if not OPTS.netlist_only: if not OPTS.netlist_only:
output_extensions.extend(["lef"]) output_extensions.extend(["lef","gds"])
# Only output gds if final routing
if OPTS.route_supplies:
output_extensions.extend(["gds"])
output_files = ["{0}{1}.{2}".format(OPTS.output_path,OPTS.output_name,x) for x in output_extensions] output_files = ["{0}{1}.{2}".format(OPTS.output_path,OPTS.output_name,x) for x in output_extensions]
debug.print_raw("Output files are: ") debug.print_raw("Output files are: ")

View File

@ -258,7 +258,7 @@ class pinv(pgate.pgate):
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load) c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"] freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["inv_leakage"] power_leak = spice["inv_leakage"]
@ -269,7 +269,7 @@ class pinv(pgate.pgate):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
transition_prob = spice["inv_transition_prob"] transition_prob = 0.5
return transition_prob*(c_load + c_para) return transition_prob*(c_load + c_para)
def input_load(self): def input_load(self):

View File

@ -233,7 +233,7 @@ class pnand2(pgate.pgate):
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load) c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"] freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand2_leakage"] power_leak = spice["nand2_leakage"]
@ -244,7 +244,7 @@ class pnand2(pgate.pgate):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
transition_prob = spice["nand2_transition_prob"] transition_prob = 0.1875
return transition_prob*(c_load + c_para) return transition_prob*(c_load + c_para)
def input_load(self): def input_load(self):

View File

@ -246,7 +246,7 @@ class pnand3(pgate.pgate):
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load) c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"] freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nand3_leakage"] power_leak = spice["nand3_leakage"]
@ -257,7 +257,7 @@ class pnand3(pgate.pgate):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
transition_prob = spice["nand3_transition_prob"] transition_prob = 0.1094
return transition_prob*(c_load + c_para) return transition_prob*(c_load + c_para)
def input_load(self): def input_load(self):

View File

@ -230,7 +230,7 @@ class pnor2(pgate.pgate):
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
c_eff = self.calculate_effective_capacitance(load) c_eff = self.calculate_effective_capacitance(load)
freq = spice["default_event_rate"] freq = spice["default_event_frequency"]
power_dyn = self.calc_dynamic_power(corner, c_eff, freq) power_dyn = self.calc_dynamic_power(corner, c_eff, freq)
power_leak = spice["nor2_leakage"] power_leak = spice["nor2_leakage"]
@ -241,7 +241,7 @@ class pnor2(pgate.pgate):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
transition_prob = spice["nor2_transition_prob"] transition_prob = 0.1875
return transition_prob*(c_load + c_para) return transition_prob*(c_load + c_para)
def build_graph(self, graph, inst_name, port_nets): def build_graph(self, graph, inst_name, port_nets):

View File

@ -75,13 +75,12 @@ class sram():
self.lef_write(lefname) self.lef_write(lefname)
print_time("LEF", datetime.datetime.now(), start_time) print_time("LEF", datetime.datetime.now(), start_time)
if OPTS.route_supplies: # Write the layout
# Write the layout start_time = datetime.datetime.now()
start_time = datetime.datetime.now() gdsname = OPTS.output_path + self.s.name + ".gds"
gdsname = OPTS.output_path + self.s.name + ".gds" debug.print_raw("GDS: Writing to {0}".format(gdsname))
debug.print_raw("GDS: Writing to {0}".format(gdsname)) self.gds_write(gdsname)
self.gds_write(gdsname) print_time("GDS", datetime.datetime.now(), start_time)
print_time("GDS", datetime.datetime.now(), start_time)
# Save the spice file # Save the spice file
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
@ -130,4 +129,4 @@ class sram():
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)) debug.print_raw("Verilog: Writing to {0}".format(vname))
self.verilog_write(vname) self.verilog_write(vname)
print_time("Verilog", datetime.datetime.now(), start_time) print_time("Verilog", datetime.datetime.now(), start_time)

View File

@ -70,46 +70,51 @@ class sram_1bank(sram_base):
wmask_pos = [None]*len(self.all_ports) wmask_pos = [None]*len(self.all_ports)
data_pos = [None]*len(self.all_ports) data_pos = [None]*len(self.all_ports)
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk if self.write_size:
# The M1 pitch is for supply rail spacings max_gap_size = self.m3_pitch*self.word_size + 2*self.m1_pitch
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch max_gap_size_wmask = self.m2_pitch*max(self.num_wmasks+1,self.col_addr_size+1) + 2*self.m1_pitch
else:
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
# The M1 pitch is for supply rail spacings
max_gap_size = self.m2_pitch*max(self.word_size+1,self.col_addr_size+1) + 2*self.m1_pitch
# Port 0 # Port 0
port = 0 port = 0
if self.write_size: if port in self.write_ports:
if port in self.write_ports: if self.write_size:
# Add the write mask flops below the write mask AND array. # Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(self.bank.bank_array_ll.x, wmask_pos[port] = vector(self.bank.bank_array_ll.x,
-0.5*max_gap_size - self.dff.height) -max_gap_size_wmask - self.dff.height)
self.wmask_dff_insts[port].place(wmask_pos[port]) self.wmask_dff_insts[port].place(wmask_pos[port])
# Add the data flops below the write mask flops. # Add the data flops below the write mask flops.
data_pos[port] = vector(self.bank.bank_array_ll.x, data_pos[port] = vector(self.bank.bank_array_ll.x,
-1.5*max_gap_size - 2*self.dff.height) -max_gap_size - max_gap_size_wmask - 2*self.dff.height)
self.data_dff_insts[port].place(data_pos[port]) self.data_dff_insts[port].place(data_pos[port])
else: else:
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) # Add the data flops below the bank to the right of the lower-left of bank array
data_pos[port] = vector(self.bank.bank_array_ll.x,0) # This relies on the lower-left of the array of the bank
# decoder in upper left, bank in upper right, sensing in lower right.
# These flops go below the sensing and leave a gap to channel route to the
# sense amps.
if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ll.x,
-max_gap_size - self.dff.height)
self.data_dff_insts[port].place(data_pos[port])
else: else:
# Add the data flops below the bank to the right of the lower-left of bank array wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
# This relies on the lower-left of the array of the bank data_pos[port] = vector(self.bank.bank_array_ll.x,0)
# decoder in upper left, bank in upper right, sensing in lower right.
# These flops go below the sensing and leave a gap to channel route to the
# sense amps.
if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ll.x,
-max_gap_size - self.dff.height)
self.data_dff_insts[port].place(data_pos[port])
else:
data_pos[port] = vector(self.bank.bank_array_ll.x,0)
# Add the col address flops below the bank to the left of the lower-left of bank array # Add the col address flops below the bank to the left of the lower-left of bank array
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, if self.write_size:
-max_gap_size - self.col_addr_dff_insts[port].height) col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-max_gap_size_wmask - self.col_addr_dff_insts[port].height)
else:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-max_gap_size - self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port]) self.col_addr_dff_insts[port].place(col_addr_pos[port])
else: else:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x,0) col_addr_pos[port] = vector(self.bank.bank_array_ll.x,0)
@ -125,13 +130,6 @@ class sram_1bank(sram_base):
y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height) y_offset = max(self.control_logic_insts[port].uy(), self.bank_inst.uy() - self.row_addr_dff_insts[port].height)
row_addr_pos[port] = vector(x_offset, y_offset) row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(row_addr_pos[port]) self.row_addr_dff_insts[port].place(row_addr_pos[port])
# Add the col address flops below the bank to the left of the lower-left of bank array
if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-max_gap_size - self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port])
if len(self.all_ports)>1: if len(self.all_ports)>1:
# Port 1 # Port 1
@ -140,15 +138,14 @@ class sram_1bank(sram_base):
if port in self.write_ports: if port in self.write_ports:
if self.write_size: if self.write_size:
# Add the write mask flops below the write mask AND array. # Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width,
self.bank.height + 0.5*max_gap_size + self.dff.height) self.bank.height + max_gap_size_wmask + self.dff.height)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX") self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
# Add the data flops below the write mask flops # Add the data flops below the write mask flops
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + 1.5*max_gap_size + 2*self.dff.height) self.bank.height + max_gap_size_wmask + max_gap_size + 2*self.dff.height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX") self.data_dff_insts[port].place(data_pos[port], mirror="MX")
else: else:
# Add the data flops above the bank to the left of the upper-right of bank array # Add the data flops above the bank to the left of the upper-right of bank array
# This relies on the upper-right of the array of the bank # This relies on the upper-right of the array of the bank
@ -161,8 +158,12 @@ class sram_1bank(sram_base):
# Add the col address flops above the bank to the right of the upper-right of bank array # Add the col address flops above the bank to the right of the upper-right of bank array
if self.col_addr_dff: if self.col_addr_dff:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, if self.write_size:
self.bank.height + max_gap_size + self.dff.height) col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
self.bank.height + max_gap_size_wmask + self.dff.height)
else:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
self.bank.height + max_gap_size + self.dff.height)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX") self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
else: else:
col_addr_pos[port] = self.bank_inst.ur() col_addr_pos[port] = self.bank_inst.ur()
@ -354,11 +355,16 @@ class sram_1bank(sram_base):
""" Connect the output of the data flops to the write driver """ """ Connect the output of the data flops to the write driver """
# This is where the channel will start (y-dimension at least) # This is where the channel will start (y-dimension at least)
for port in self.write_ports: for port in self.write_ports:
if port%2: if self.write_size:
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch) if port % 2:
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2)*self.m3_pitch)
else:
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch)
else: else:
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch) if port%2:
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size+2)*self.m1_pitch)
else:
offset = self.data_dff_insts[port].ul() + vector(0, 2*self.m1_pitch)
dff_names = ["dout_{}".format(x) for x in range(self.word_size)] dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
@ -377,7 +383,10 @@ class sram_1bank(sram_base):
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
if self.write_size: if self.write_size:
for x in bank_names: for x in bank_names:
pin_offset = self.bank_inst.get_pin(x).bc() if port % 2:
pin_offset = self.bank_inst.get_pin(x).uc()
else:
pin_offset = self.bank_inst.get_pin(x).bc()
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pin_offset) offset=pin_offset)
self.add_via_center(layers=("metal2", "via2", "metal3"), self.add_via_center(layers=("metal2", "via2", "metal3"),
@ -398,7 +407,7 @@ class sram_1bank(sram_base):
# This is where the channel will start (y-dimension at least) # This is where the channel will start (y-dimension at least)
for port in self.write_ports: for port in self.write_ports:
if port % 2: if port % 2:
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch) offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks+2) * self.m1_pitch)
else: else:
offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch) offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)

View File

@ -27,7 +27,7 @@ class sram_base(design, verilog, lef):
""" """
def __init__(self, name, sram_config): def __init__(self, name, sram_config):
design.__init__(self, name) design.__init__(self, name)
lef.__init__(self, ["metal1", "metal2", "metal3"]) lef.__init__(self, ["metal1", "metal2", "metal3", "metal4"])
verilog.__init__(self) verilog.__init__(self)
self.sram_config = sram_config self.sram_config = sram_config
@ -120,7 +120,7 @@ class sram_base(design, verilog, lef):
self.add_lvs_correspondence_points() self.add_lvs_correspondence_points()
#self.offset_all_coordinates() self.offset_all_coordinates()
highest_coord = self.find_highest_coords() highest_coord = self.find_highest_coords()
self.width = highest_coord[0] self.width = highest_coord[0]

View File

@ -0,0 +1,61 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 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
from sram_factory import factory
import debug
# @unittest.skip("SKIPPING psram_1bank_2mux_1rw_1w_wmask_test, multiport layout not complete")
class psram_1bank_2mux_1rw_1w_wmask_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
OPTS.bitcell = "pbitcell"
OPTS.replica_bitcell = "replica_pbitcell"
OPTS.dummy_bitcell = "dummy_pbitcell"
OPTS.num_rw_ports = 1
OPTS.num_w_ports = 1
OPTS.num_r_ports = 0
c = sram_config(word_size=8,
write_size=4,
num_words=32,
num_banks=1)
c.num_words = 32
c.words_per_row = 2
c.recompute_sizes()
debug.info(1, "Layout test for {}rw,{}r,{}w psram "
"with {} bit words, {} words, {} words per "
"row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.words_per_row,
c.num_banks))
a = factory.create(module_type="sram", sram_config=c)
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,53 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 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
from sram_factory import factory
import debug
@unittest.skip("SKIPPING sram_1bank_32b_1024_wmask_test")
class sram_1bank_32b_1024_wmask_test(openram_test):
def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name))
from sram_config import sram_config
c = sram_config(word_size=32,
write_size=8,
num_words=1024,
num_banks=1)
c.recompute_sizes()
debug.info(1, "Layout test for {}rw,{}r,{}w sram "
"with {} bit words, {} words, {} bit writes, {} words per "
"row, {} banks".format(OPTS.num_rw_ports,
OPTS.num_r_ports,
OPTS.num_w_ports,
c.word_size,
c.num_words,
c.write_size,
c.words_per_row,
c.num_banks))
a = factory.create(module_type="sram", sram_config=c)
self.local_check(a, final_verification=True)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -54,7 +54,7 @@ class timing_sram_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner) d = delay(s.s, tempspice, corner)
import tech import tech
loads = [tech.spice["msflop_in_cap"]*4] loads = [tech.spice["dff_in_cap"]*4]
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
data, port_data = d.analyze(probe_address, probe_data, slews, loads) data, port_data = d.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data #Combine info about port into all data

View File

@ -49,7 +49,7 @@ class model_delay_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner) d = delay(s.s, tempspice, corner)
import tech import tech
loads = [tech.spice["msflop_in_cap"]*4] loads = [tech.spice["dff_in_cap"]*4]
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
# Run a spice characterization # Run a spice characterization

View File

@ -47,7 +47,7 @@ class timing_sram_test(openram_test):
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner) d = delay(s.s, tempspice, corner)
import tech import tech
loads = [tech.spice["msflop_in_cap"]*4] loads = [tech.spice["dff_in_cap"]*4]
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
data, port_data = d.analyze(probe_address, probe_data, slews, loads) data, port_data = d.analyze(probe_address, probe_data, slews, loads)
#Combine info about port into all data #Combine info about port into all data

View File

@ -19,6 +19,7 @@ class lib_model_corners_lib_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name)) globals.init_openram("config_{0}".format(OPTS.tech_name))
OPTS.netlist_only = True
from characterizer import lib from characterizer import lib
from sram import sram from sram import sram

View File

@ -19,7 +19,8 @@ class lib_sram_model_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_{0}".format(OPTS.tech_name)) globals.init_openram("config_{0}".format(OPTS.tech_name))
OPTS.netlist_only = True
from characterizer import lib from characterizer import lib
from sram import sram from sram import sram
from sram_config import sram_config from sram_config import sram_config

View File

@ -38,9 +38,12 @@ class openram_back_end_test(openram_test):
os.chmod(out_path, 0o0750) os.chmod(out_path, 0o0750)
# specify the same verbosity for the system call # specify the same verbosity for the system call
verbosity = "" options = ""
for i in range(OPTS.debug_level): for i in range(OPTS.debug_level):
verbosity += " -v" options += " -v"
if OPTS.spice_name:
options += " -s {}".format(OPTS.spice_name)
# Always perform code coverage # Always perform code coverage
if OPTS.coverage == 0: if OPTS.coverage == 0:
@ -52,7 +55,7 @@ class openram_back_end_test(openram_test):
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file, out_file,
out_path, out_path,
verbosity, options,
config_name, config_name,
out_path) out_path)
debug.info(1, cmd) debug.info(1, cmd)
@ -83,10 +86,11 @@ class openram_back_end_test(openram_test):
# now clean up the directory # now clean up the directory
if os.path.exists(out_path): if OPTS.purge_temp:
shutil.rmtree(out_path, ignore_errors=True) if os.path.exists(out_path):
self.assertEqual(os.path.exists(out_path),False) shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False)
globals.end_openram() globals.end_openram()
# run the test from the command line # run the test from the command line

View File

@ -39,10 +39,12 @@ class openram_front_end_test(openram_test):
os.chmod(out_path, 0o0750) os.chmod(out_path, 0o0750)
# specify the same verbosity for the system call # specify the same verbosity for the system call
verbosity = "" options = ""
for i in range(OPTS.debug_level): for i in range(OPTS.debug_level):
verbosity += " -v" options += " -v"
if OPTS.spice_name:
options += " -s {}".format(OPTS.spice_name)
# Always perform code coverage # Always perform code coverage
if OPTS.coverage == 0: if OPTS.coverage == 0:
@ -54,22 +56,17 @@ class openram_front_end_test(openram_test):
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file, out_file,
out_path, out_path,
verbosity, options,
config_name, config_name,
out_path) out_path)
debug.info(1, cmd) debug.info(1, cmd)
os.system(cmd) os.system(cmd)
# assert an error until we actually check a result # assert an error until we actually check a result
for extension in ["v", "lef", "sp"]: for extension in ["v", "lef", "sp", "gds"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension) filename = "{0}/{1}.{2}".format(out_path,out_file,extension)
debug.info(1,"Checking for file: " + filename) debug.info(1,"Checking for file: " + filename)
self.assertEqual(os.path.exists(filename),True) self.assertEqual(os.path.exists(filename),True)
# assert an error if we output the incomplete gds!
for extension in ["gds"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension)
debug.info(1,"Checking file does NOT exist: " + filename)
self.assertEqual(os.path.exists(filename),False)
# Make sure there is any .lib file # Make sure there is any .lib file
import glob import glob
@ -89,10 +86,11 @@ class openram_front_end_test(openram_test):
self.assertEqual(len(re.findall('WARNING',output)),0) self.assertEqual(len(re.findall('WARNING',output)),0)
# now clean up the directory # now clean up the directory
if os.path.exists(out_path): if OPTS.purge_temp:
shutil.rmtree(out_path, ignore_errors=True) if os.path.exists(out_path):
self.assertEqual(os.path.exists(out_path),False) shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False)
globals.end_openram() globals.end_openram()

View File

@ -303,37 +303,16 @@ spice["fall_time"] = 0.005 # fall time in [Nano-seconds]
spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius)
spice["nom_temperature"] = 25 # Nominal temperature (celcius) spice["nom_temperature"] = 25 # Nominal temperature (celcius)
#sram signal names
#FIXME: We don't use these everywhere...
spice["vdd_name"] = "vdd"
spice["gnd_name"] = "gnd"
spice["control_signals"] = ["CSB", "WEB"]
spice["data_name"] = "DATA"
spice["addr_name"] = "ADDR"
spice["minwidth_tx"] = drc["minwidth_tx"]
spice["channel"] = drc["minlength_channel"]
spice["clk"] = "clk"
# analytical delay parameters # analytical delay parameters
spice["vdd_nominal"] = 1.0 # Typical Threshold voltage in Volts spice["nom_threshold"] = 0.4 # Typical Threshold voltage in Volts
spice["temp_nominal"] = 25.0 # Typical Threshold voltage in Volts
spice["v_threshold_typical"] = 0.4 # Typical Threshold voltage in Volts
spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square
spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2
spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms
spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff
spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff spice["min_tx_gate_c"] = 0.2 # Minimum transistor gate capacitance in ff
spice["msflop_setup"] = 9 # DFF setup time in ps
spice["msflop_hold"] = 1 # DFF hold time in ps
spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps
spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load
spice["msflop_in_cap"] = 0.2091 # Input capacitance of ms_flop (Din) [Femto-farad]
spice["dff_setup"] = 9 # DFF setup time in ps spice["dff_setup"] = 9 # DFF setup time in ps
spice["dff_hold"] = 1 # DFF hold time in ps spice["dff_hold"] = 1 # DFF hold time in ps
spice["dff_delay"] = 20.5 # DFF Clk-to-q delay in ps spice["dff_in_cap"] = 0.2091 # Input capacitance (D) [Femto-farad]
spice["dff_slew"] = 13.1 # DFF output slew in ps w/ no load spice["dff_out_cap"] = 2 # Output capacitance (Q) [Femto-farad]
spice["dff_in_cap"] = 0.2091 # Input capacitance of ms_flop (Din) [Femto-farad]
# analytical power parameters, many values are temporary # analytical power parameters, many values are temporary
spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW
@ -341,19 +320,13 @@ spice["inv_leakage"] = 1 # Leakage power of inverter in nW
spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW
spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW
spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW
spice["msflop_leakage"] = 1 # Leakage power of flop in nW spice["dff_leakage"] = 1 # Leakage power of flop in nW
spice["flop_para_cap"] = 2 # Parasitic Output capacitance in fF
spice["default_event_rate"] = 100 # Default event activity of every gate. MHz spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz
spice["flop_transition_prob"] = 0.5 # Transition probability of inverter.
spice["inv_transition_prob"] = 0.5 # Transition probability of inverter.
spice["nand2_transition_prob"] = 0.1875 # Transition probability of 2-input nand.
spice["nand3_transition_prob"] = 0.1094 # Transition probability of 3-input nand.
spice["nor2_transition_prob"] = 0.1875 # Transition probability of 2-input nor.
#Parameters related to sense amp enable timing and delay chain/RBL sizing #Parameters related to sense amp enable timing and delay chain/RBL sizing
parameter['le_tau'] = 2.25 #In pico-seconds. parameter["le_tau"] = 2.25 #In pico-seconds.
parameter['cap_relative_per_ff'] = 7.5 #Units of Relative Capacitance/ Femto-Farad parameter["cap_relative_per_ff"] = 7.5 #Units of Relative Capacitance/ Femto-Farad
parameter["dff_clk_cin"] = 30.6 #relative capacitance parameter["dff_clk_cin"] = 30.6 #relative capacitance
parameter["6tcell_wl_cin"] = 3 #relative capacitance parameter["6tcell_wl_cin"] = 3 #relative capacitance
parameter["min_inv_para_delay"] = 2.4 #Tau delay units parameter["min_inv_para_delay"] = 2.4 #Tau delay units
@ -361,7 +334,7 @@ parameter["sa_en_pmos_size"] = 0.72 #micro-meters
parameter["sa_en_nmos_size"] = 0.27 #micro-meters parameter["sa_en_nmos_size"] = 0.27 #micro-meters
parameter["sa_inv_pmos_size"] = 0.54 #micro-meters parameter["sa_inv_pmos_size"] = 0.54 #micro-meters
parameter["sa_inv_nmos_size"] = 0.27 #micro-meters parameter["sa_inv_nmos_size"] = 0.27 #micro-meters
parameter['bitcell_drain_cap'] = 0.1 #In Femto-Farad, approximation of drain capacitance parameter["bitcell_drain_cap"] = 0.1 #In Femto-Farad, approximation of drain capacitance
################################################### ###################################################
##END Spice Simulation Parameters ##END Spice Simulation Parameters

File diff suppressed because it is too large Load Diff

View File

@ -44,4 +44,14 @@ Contributions and modifications to this kit are welcomed and encouraged.
ncsu_basekit/ Base kit for custom design ncsu_basekit/ Base kit for custom design
osu_soc/ Standard-cell kit for synthesis, place, & route osu_soc/ Standard-cell kit for synthesis, place, & route
FreePDK45.lyp is converted automatically from the .tf using:
https://github.com/klayoutmatthias/tf_import
Command line:
klayout -z -rd tf_file=FreePDK45.tf -rd lyp_file=FreePDK45.lyp
You can then view layouts with:
klayout file.gds -l mosis.lyp
glade_freepdk45.py is a script for Glade:
https://peardrop.co.uk/
to load the .tf using:
glade -script ~/openram/technology/freepdk45/tf/glade_freepdk45.py -gds file.gds

View File

@ -1223,6 +1223,7 @@ drDefinePacket(
( display prBoundary blank solid purple purple ) ( display prBoundary blank solid purple purple )
( display prBoundaryBnd blank solid cyan cyan ) ( display prBoundaryBnd blank solid cyan cyan )
( display prBoundaryLbl blank solid purple purple ) ( display prBoundaryLbl blank solid purple purple )
( display comment blank solid purple purple )
( display align blank solid tan tan ) ( display align blank solid tan tan )
( display hardFence blank solid red red ) ( display hardFence blank solid red red )
( display softFence blank solid yellow yellow ) ( display softFence blank solid yellow yellow )

View File

@ -219,8 +219,6 @@ spice["nmos"]="n"
spice["pmos"]="p" spice["pmos"]="p"
# This is a map of corners to model files # This is a map of corners to model files
SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR") SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR")
# FIXME: Uncomment when we have the new spice models
#spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"] }
spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"],
"FF" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], "FF" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"],
"FS" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], "FS" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"],
@ -242,37 +240,17 @@ spice["fall_time"] = 0.05 # fall time in [Nano-seconds]
spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius)
spice["nom_temperature"] = 25 # Nominal temperature (celcius) spice["nom_temperature"] = 25 # Nominal temperature (celcius)
#sram signal names
#FIXME: We don't use these everywhere...
spice["vdd_name"] = "vdd"
spice["gnd_name"] = "gnd"
spice["control_signals"] = ["CSB", "WEB"]
spice["data_name"] = "DATA"
spice["addr_name"] = "ADDR"
spice["minwidth_tx"] = drc["minwidth_tx"]
spice["channel"] = drc["minlength_channel"]
spice["clk"] = "clk"
# analytical delay parameters # analytical delay parameters
spice["nom_threshold"] = 1.3 # Typical Threshold voltage in Volts
# FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45.
spice["vdd_nominal"] = 5.0 # Typical Threshold voltage in Volts
spice["temp_nominal"] = 25.0 # Typical Threshold voltage in Volts
spice["v_threshold_typical"] = 1.3 # Typical Threshold voltage in Volts
spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square
spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2
spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms
spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff
spice["min_tx_gate_c"] = 0.1 # Minimum transistor gate capacitance in ff spice["min_tx_gate_c"] = 0.1 # Minimum transistor gate capacitance in ff
spice["msflop_setup"] = 9 # DFF setup time in ps
spice["msflop_hold"] = 1 # DFF hold time in ps
spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps
spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load
spice["msflop_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad]
spice["dff_setup"] = 9 # DFF setup time in ps spice["dff_setup"] = 9 # DFF setup time in ps
spice["dff_hold"] = 1 # DFF hold time in ps spice["dff_hold"] = 1 # DFF hold time in ps
spice["dff_delay"] = 20.5 # DFF Clk-to-q delay in ps spice["dff_in_cap"] = 9.8242 # Input capacitance (D) [Femto-farad]
spice["dff_slew"] = 13.1 # DFF output slew in ps w/ no load spice["dff_out_cap"] = 2 # Output capacitance (Q) [Femto-farad]
spice["dff_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad]
# analytical power parameters, many values are temporary # analytical power parameters, many values are temporary
spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW
@ -280,27 +258,21 @@ spice["inv_leakage"] = 1 # Leakage power of inverter in nW
spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW
spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW
spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW
spice["msflop_leakage"] = 1 # Leakage power of flop in nW spice["dff_leakage"] = 1 # Leakage power of flop in nW
spice["flop_para_cap"] = 2 # Parasitic Output capacitance in fF
spice["default_event_rate"] = 100 # Default event activity of every gate. MHz spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz
spice["flop_transition_prob"] = 0.5 # Transition probability of inverter.
spice["inv_transition_prob"] = 0.5 # Transition probability of inverter.
spice["nand2_transition_prob"] = 0.1875 # Transition probability of 2-input nand.
spice["nand3_transition_prob"] = 0.1094 # Transition probability of 3-input nand.
spice["nor2_transition_prob"] = 0.1875 # Transition probability of 2-input nor.
#Logical Effort relative values for the Handmade cells #Logical Effort relative values for the Handmade cells
parameter['le_tau'] = 23 #In pico-seconds. parameter["le_tau"] = 23 #In pico-seconds.
parameter["min_inv_para_delay"] = 0.73 #In relative delay units parameter["min_inv_para_delay"] = 0.73 #In relative delay units
parameter['cap_relative_per_ff'] = 0.91 #Units of Relative Capacitance/ Femto-Farad parameter["cap_relative_per_ff"] = 0.91 #Units of Relative Capacitance/ Femto-Farad
parameter["dff_clk_cin"] = 27.5 #In relative capacitance units parameter["dff_clk_cin"] = 27.5 #In relative capacitance units
parameter["6tcell_wl_cin"] = 2 #In relative capacitance units parameter["6tcell_wl_cin"] = 2 #In relative capacitance units
parameter["sa_en_pmos_size"] = 24*_lambda_ parameter["sa_en_pmos_size"] = 24*_lambda_
parameter["sa_en_nmos_size"] = 9*_lambda_ parameter["sa_en_nmos_size"] = 9*_lambda_
parameter["sa_inv_pmos_size"] = 18*_lambda_ parameter["sa_inv_pmos_size"] = 18*_lambda_
parameter["sa_inv_nmos_size"] = 9*_lambda_ parameter["sa_inv_nmos_size"] = 9*_lambda_
parameter['bitcell_drain_cap'] = 0.2 #In Femto-Farad, approximation of drain capacitance parameter["bitcell_drain_cap"] = 0.2 #In Femto-Farad, approximation of drain capacitance
################################################### ###################################################
##END Spice Simulation Parameters ##END Spice Simulation Parameters

View File

@ -70,6 +70,7 @@ parameter={}
parameter["min_tx_size"] = 4*_lambda_ parameter["min_tx_size"] = 4*_lambda_
parameter["beta"] = 2 parameter["beta"] = 2
# These 6T sizes are used in the parameterized bitcell.
parameter["6T_inv_nmos_size"] = 8*_lambda_ parameter["6T_inv_nmos_size"] = 8*_lambda_
parameter["6T_inv_pmos_size"] = 3*_lambda_ parameter["6T_inv_pmos_size"] = 3*_lambda_
parameter["6T_access_size"] = 4*_lambda_ parameter["6T_access_size"] = 4*_lambda_
@ -246,8 +247,6 @@ spice["nmos"]="n"
spice["pmos"]="p" spice["pmos"]="p"
# This is a map of corners to model files # This is a map of corners to model files
SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR") SPICE_MODEL_DIR=os.environ.get("SPICE_MODEL_DIR")
# FIXME: Uncomment when we have the new spice models
#spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"] }
spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"], spice["fet_models"] = { "TT" : [SPICE_MODEL_DIR+"/nom/pmos.sp",SPICE_MODEL_DIR+"/nom/nmos.sp"],
"FF" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"], "FF" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ff/nmos.sp"],
"FS" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"], "FS" : [SPICE_MODEL_DIR+"/ff/pmos.sp",SPICE_MODEL_DIR+"/ss/nmos.sp"],
@ -269,37 +268,17 @@ spice["fall_time"] = 0.05 # fall time in [Nano-seconds]
spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius) spice["temperatures"] = [0, 25, 100] # Temperature corners (celcius)
spice["nom_temperature"] = 25 # Nominal temperature (celcius) spice["nom_temperature"] = 25 # Nominal temperature (celcius)
#sram signal names
#FIXME: We don't use these everywhere...
spice["vdd_name"] = "vdd"
spice["gnd_name"] = "gnd"
spice["control_signals"] = ["CSB", "WEB"]
spice["data_name"] = "DATA"
spice["addr_name"] = "ADDR"
spice["minwidth_tx"] = drc["minwidth_tx"]
spice["channel"] = drc["minlength_channel"]
spice["clk"] = "clk"
# analytical delay parameters # analytical delay parameters
spice["nom_threshold"] = 1.3 # Nominal Threshold voltage in Volts
# FIXME: These need to be updated for SCMOS, they are copied from FreePDK45. # FIXME: These need to be updated for SCMOS, they are copied from FreePDK45.
spice["vdd_nominal"] = 5.0 # Typical Threshold voltage in Volts
spice["temp_nominal"] = 25.0 # Typical Threshold voltage in Volts
spice["v_threshold_typical"] = 1.3 # Typical Threshold voltage in Volts
spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square spice["wire_unit_r"] = 0.075 # Unit wire resistance in ohms/square
spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2 spice["wire_unit_c"] = 0.64 # Unit wire capacitance ff/um^2
spice["min_tx_r"] = 9250.0 # Minimum transistor on resistance in ohms
spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff spice["min_tx_drain_c"] = 0.7 # Minimum transistor drain capacitance in ff
spice["min_tx_gate_c"] = 0.1 # Minimum transistor gate capacitance in ff spice["min_tx_gate_c"] = 0.1 # Minimum transistor gate capacitance in ff
spice["msflop_setup"] = 9 # DFF setup time in ps
spice["msflop_hold"] = 1 # DFF hold time in ps
spice["msflop_delay"] = 20.5 # DFF Clk-to-q delay in ps
spice["msflop_slew"] = 13.1 # DFF output slew in ps w/ no load
spice["msflop_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad]
spice["dff_setup"] = 9 # DFF setup time in ps spice["dff_setup"] = 9 # DFF setup time in ps
spice["dff_hold"] = 1 # DFF hold time in ps spice["dff_hold"] = 1 # DFF hold time in ps
spice["dff_delay"] = 20.5 # DFF Clk-to-q delay in ps spice["dff_in_cap"] = 9.8242 # Input capacitance (D) [Femto-farad]
spice["dff_slew"] = 13.1 # DFF output slew in ps w/ no load spice["dff_out_cap"] = 2 # Output capacitance (Q) [Femto-farad]
spice["dff_in_cap"] = 9.8242 # Input capacitance of ms_flop (Din) [Femto-farad]
# analytical power parameters, many values are temporary # analytical power parameters, many values are temporary
spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW
@ -307,27 +286,21 @@ spice["inv_leakage"] = 1 # Leakage power of inverter in nW
spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW
spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW
spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW
spice["msflop_leakage"] = 1 # Leakage power of flop in nW spice["dff_leakage"] = 1 # Leakage power of flop in nW
spice["flop_para_cap"] = 2 # Parasitic Output capacitance in fF
spice["default_event_rate"] = 100 # Default event activity of every gate. MHz spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz
spice["flop_transition_prob"] = .5 # Transition probability of inverter.
spice["inv_transition_prob"] = .5 # Transition probability of inverter.
spice["nand2_transition_prob"] = .1875 # Transition probability of 2-input nand.
spice["nand3_transition_prob"] = .1094 # Transition probability of 3-input nand.
spice["nor2_transition_prob"] = .1875 # Transition probability of 2-input nor.
#Logical Effort relative values for the Handmade cells #Logical Effort relative values for the Handmade cells
parameter['le_tau'] = 23 #In pico-seconds. parameter["le_tau"] = 23 #In pico-seconds.
parameter["min_inv_para_delay"] = .73 #In relative delay units parameter["min_inv_para_delay"] = .73 #In relative delay units
parameter['cap_relative_per_ff'] = .91 #Units of Relative Capacitance/ Femto-Farad parameter["cap_relative_per_ff"] = .91 #Units of Relative Capacitance/ Femto-Farad
parameter["dff_clk_cin"] = 27.5 #In relative capacitance units parameter["dff_clk_cin"] = 27.5 #In relative capacitance units
parameter["6tcell_wl_cin"] = 2 #In relative capacitance units parameter["6tcell_wl_cin"] = 2 #In relative capacitance units
parameter["sa_en_pmos_size"] = 24*_lambda_ parameter["sa_en_pmos_size"] = 24*_lambda_
parameter["sa_en_nmos_size"] = 9*_lambda_ parameter["sa_en_nmos_size"] = 9*_lambda_
parameter["sa_inv_pmos_size"] = 18*_lambda_ parameter["sa_inv_pmos_size"] = 18*_lambda_
parameter["sa_inv_nmos_size"] = 9*_lambda_ parameter["sa_inv_nmos_size"] = 9*_lambda_
parameter['bitcell_drain_cap'] = 0.2 #In Femto-Farad, approximation of drain capacitance parameter["bitcell_drain_cap"] = 0.2 #In Femto-Farad, approximation of drain capacitance
################################################### ###################################################
##END Spice Simulation Parameters ##END Spice Simulation Parameters

View File

@ -18,4 +18,16 @@ be found in the file cdb2oa/OA_Conversion.txt.
This kit is not yet fully supported. Please post problems and solutions at This kit is not yet fully supported. Please post problems and solutions at
http://www.chiptalk.org -> Forums -> NCSU CDK -> NCSU CDK 1.6.0.beta for Virtuoso 6.1 http://www.chiptalk.org -> Forums -> NCSU CDK -> NCSU CDK 1.6.0.beta for Virtuoso 6.1
Modified 2018 by MRG to contain SCN4ME Via3/Metal4 layers. Modified 2018 by MRG to contain SCN4ME Via3/Metal4 layers.
mosis.lyp is converted automatically from the .tf using:
https://github.com/klayoutmatthias/tf_import
Command line:
klayout -z -rd tf_file=FreePDK45.tf -rd lyp_file=FreePDK45.ly
You can then view layouts with:
klayout file.gds -l mosis.lyp
glade_scn4m_subm.py is a script for Glade:
https://peardrop.co.uk/
to load the .tf using:
glade -script ~/openram/technology/scn3me_subm/tf/glade_scn3me_subm.py -gds file.gds

File diff suppressed because it is too large Load Diff

View File

@ -552,6 +552,8 @@ layerRules(
( ("P1Con" "drawing") 47 0 t ) ( ("P1Con" "drawing") 47 0 t )
( ("Metal1" "drawing") 49 0 t ) ( ("Metal1" "drawing") 49 0 t )
( ("Metal2" "drawing") 51 0 t ) ( ("Metal2" "drawing") 51 0 t )
( ("Metal3" "drawing") 62 0 t )
( ("Metal4" "drawing") 31 0 t )
( ("annotate" "drawing") 0 0 nil ) ( ("annotate" "drawing") 0 0 nil )
( ("annotate" "drawing1") 0 0 nil ) ( ("annotate" "drawing1") 0 0 nil )
( ("annotate" "drawing2") 0 0 nil ) ( ("annotate" "drawing2") 0 0 nil )
@ -562,7 +564,6 @@ layerRules(
( ("annotate" "drawing7") 0 0 nil ) ( ("annotate" "drawing7") 0 0 nil )
( ("annotate" "drawing8") 0 0 nil ) ( ("annotate" "drawing8") 0 0 nil )
( ("annotate" "drawing9") 0 0 nil ) ( ("annotate" "drawing9") 0 0 nil )
( ("Via" "drawing") 50 0 t )
( ("Glass" "drawing") 52 0 t ) ( ("Glass" "drawing") 52 0 t )
( ("XP" "drawing") 60 0 t ) ( ("XP" "drawing") 60 0 t )
( ("Metal2" "pin") 0 0 nil ) ( ("Metal2" "pin") 0 0 nil )
@ -590,8 +591,9 @@ layerRules(
( ("P1Con" "net") 0 0 nil ) ( ("P1Con" "net") 0 0 nil )
( ("Metal1" "net") 0 0 nil ) ( ("Metal1" "net") 0 0 nil )
( ("Metal2" "net") 0 0 nil ) ( ("Metal2" "net") 0 0 nil )
( ("Metal3" "net") 0 0 nil )
( ("Metal4" "net") 0 0 nil )
( ("device" "label") 0 0 nil ) ( ("device" "label") 0 0 nil )
( ("Via" "net") 0 0 nil )
( ("pin" "label") 0 0 nil ) ( ("pin" "label") 0 0 nil )
( ("text" "drawing") 63 0 t ) ( ("text" "drawing") 63 0 t )
( ("pin" "drawing") 0 0 nil ) ( ("pin" "drawing") 0 0 nil )
@ -649,11 +651,12 @@ layerRules(
( Metal2 0 0 nil ) ( Metal2 0 0 nil )
( Glass 0 0 nil ) ( Glass 0 0 nil )
( XP 0 0 nil ) ( XP 0 0 nil )
( ("Via2" "drawing") 50 0 t ) ( ("Via" "drawing") 50 0 t )
( ("Via" "net") 0 0 nil )
( ("Via2" "drawing") 61 0 t )
( ("Via2" "net") 0 0 nil ) ( ("Via2" "net") 0 0 nil )
( ("Metal3" "drawing") 50 0 t ) ( ("Via3" "drawing") 30 0 t )
( ("Metal3" "net") 0 0 nil ) ( ("Via3" "net") 0 0 nil )
( ("Metal3" "pin") 0 0 nil )
( ("CapWell" "drawing") 0 0 nil ) ( ("CapWell" "drawing") 0 0 nil )
( ("CapWell" "net") 0 0 nil ) ( ("CapWell" "net") 0 0 nil )
( ("SiBlock" "drawing") 0 0 nil ) ( ("SiBlock" "drawing") 0 0 nil )
@ -665,6 +668,7 @@ layerRules(
viaLayers( viaLayers(
;( layer1 viaLayer layer2 ) ;( layer1 viaLayer layer2 )
;( ------ -------- ------ ) ;( ------ -------- ------ )
( Metal3 Via3 Metal4 )
( Metal2 Via2 Metal3 ) ( Metal2 Via2 Metal3 )
( Metal1 Via Metal2 ) ( Metal1 Via Metal2 )
( Active ActX Poly1 ) ( Active ActX Poly1 )
@ -798,6 +802,10 @@ electricalRules(
( currentDensity "Metal1" 1.0 ) ( currentDensity "Metal1" 1.0 )
( currentDensity "Via" 1.0 ) ( currentDensity "Via" 1.0 )
( currentDensity "Metal2" 1.0 ) ( currentDensity "Metal2" 1.0 )
( currentDensity "Via2" 1.0 )
( currentDensity "Metal3" 1.0 )
( currentDensity "Via3" 1.0 )
( currentDensity "Metal4" 1.0 )
) ;characterizationRules ) ;characterizationRules
) ;electricalRules ) ;electricalRules
@ -824,10 +832,13 @@ leRules(
( Metal2 drawing ) ( Metal2 drawing )
( Via2 drawing ) ( Via2 drawing )
( Metal3 drawing ) ( Metal3 drawing )
( Via3 drawing )
( Metal4 drawing )
( Poly1 pin ) ( Poly1 pin )
( Metal1 pin ) ( Metal1 pin )
( Metal2 pin ) ( Metal2 pin )
( Metal3 pin ) ( Metal3 pin )
( Metal4 pin )
( Poly2 drawing ) ( Poly2 drawing )
( P2Con drawing ) ( P2Con drawing )
( instance drawing ) ( instance drawing )
@ -853,7 +864,7 @@ leRules(
lxRules( lxRules(
lxExtractLayers( lxExtractLayers(
(Metal1 Metal2 Metal3) (Metal1 Metal2 Metal3 Metal4)
) ;lxExtractLayers ) ;lxExtractLayers
) ;lxRules ) ;lxRules