Merge branch 'dev' into datasheet_gen

This commit is contained in:
Jesse Cirimelli-Low 2018-11-10 10:58:35 -08:00
commit 62f8d26ec6
29 changed files with 635 additions and 496 deletions

View File

@ -51,31 +51,36 @@ class design(hierarchy_design):
self.implant_space = drc("implant_to_implant") self.implant_space = drc("implant_to_implant")
def setup_multiport_constants(self): def setup_multiport_constants(self):
""" These are contants and lists that aid multiport design """ """
self.total_write = OPTS.num_rw_ports + OPTS.num_w_ports These are contants and lists that aid multiport design.
self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports Ports are always in the order RW, W, R.
self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports Port indices start from 0 and increment.
self.num_rw_ports = OPTS.num_rw_ports A first RW port will have clk0, csb0, web0, addr0, data0
A first W port (with no RW ports) will be: clk0, csb0, addr0, data0
"""
total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
# These are the read/write port indices.
self.readwrite_ports = []
# These are the read/write and write-only port indices
self.write_ports = []
# These are teh read/write and read-only port indice
self.read_ports = []
# These are all the ports
self.all_ports = list(range(total_ports))
# Port indices used for data, address, and control signals
# Port IDs used to identify port type
self.write_index = []
self.read_index = []
self.port_id = []
port_number = 0 port_number = 0
for port in range(OPTS.num_rw_ports): for port in range(OPTS.num_rw_ports):
self.write_index.append(port_number) self.readwrite_ports.append(port_number)
self.read_index.append(port_number) self.write_ports.append(port_number)
self.port_id.append("rw") self.read_ports.append(port_number)
port_number += 1 port_number += 1
for port in range(OPTS.num_w_ports): for port in range(OPTS.num_w_ports):
self.write_index.append(port_number) self.write_ports.append(port_number)
self.port_id.append("w")
port_number += 1 port_number += 1
for port in range(OPTS.num_r_ports): for port in range(OPTS.num_r_ports):
self.read_index.append(port_number) self.read_ports.append(port_number)
self.port_id.append("r")
port_number += 1 port_number += 1
def analytical_power(self, proc, vdd, temp, load): def analytical_power(self, proc, vdd, temp, load):

View File

@ -856,10 +856,10 @@ class layout(lef.lef):
# Remove the net from other constriants in the VCG # Remove the net from other constriants in the VCG
vcg=remove_net_from_graph(net_name, vcg) vcg=remove_net_from_graph(net_name, vcg)
# Add the trunk routes from the bottom up or right to left # Add the trunk routes from the bottom up or the left to right
if vertical: if vertical:
self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch) self.add_vertical_trunk_route(pin_list, offset, layer_stack, pitch)
offset -= vector(pitch,0) offset += vector(pitch,0)
else: else:
self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch) self.add_horizontal_trunk_route(pin_list, offset, layer_stack, pitch)
offset -= vector(0,pitch) offset -= vector(0,pitch)

View File

@ -317,6 +317,42 @@ class pin_layout:
else: else:
return [0,0] return [0,0]
def distance(self, other):
"""
Calculate the distance to another pin layout.
"""
(r1_ll,r1_ur) = self.rect
(r2_ll,r2_ur) = other.rect
def dist(x1, y1, x2, y2):
return sqrt((x2-x1)**2 + (y2-y1)**2)
left = r2_ur.x < r1_ll.x
right = r1_ur.x < r2_ll.x
bottom = r2_ur.y < r1_ll.y
top = r1_ur.y < r2_ll.y
if top and left:
return dist(r1_ll.x, r1_ur.y, r2_ur.x, r2_ll.y)
elif left and bottom:
return dist(r1_ll.x, r1_ll.y, r2_ur.x, r2_ur.y)
elif bottom and right:
return dist(r1_ur.x, r1_ll.y, r2_ll.x, r2_ur.y)
elif right and top:
return dist(r1_ur.x, r1_ur.y, r2_ll.x, r2_ll.y)
elif left:
return r1_ll.x - r2_ur.x
elif right:
return r2_ll.x - r1.ur.x
elif bottom:
return r1_ll.y - r2_ur.y
elif top:
return r2_ll.y - r1_ur.y
else:
# rectangles intersect
return 0
def overlap_length(self, other): def overlap_length(self, other):
""" """
Calculate the intersection segment and determine its length Calculate the intersection segment and determine its length

View File

@ -65,26 +65,6 @@ class bitcell(design.design):
column_pins = ["br"] column_pins = ["br"]
return column_pins return column_pins
def list_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
column_pins = ["bl"]
return column_pins
def list_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
column_pins = ["br"]
return column_pins
def list_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
column_pins = ["bl"]
return column_pins
def list_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
column_pins = ["br"]
return column_pins
def analytical_power(self, proc, vdd, temp, load): def analytical_power(self, proc, vdd, temp, load):
"""Bitcell power in nW. Only characterizes leakage.""" """Bitcell power in nW. Only characterizes leakage."""
from tech import spice from tech import spice

View File

@ -865,26 +865,6 @@ class pbitcell(design.design):
br_pins = self.rw_br_names + self.w_br_names + self.r_br_names br_pins = self.rw_br_names + self.w_br_names + self.r_br_names
return br_pins return br_pins
def list_read_bl_names(self):
""" Creates a list of bl pin names associated with read ports """
bl_pins = self.rw_bl_names + self.r_bl_names
return bl_pins
def list_read_br_names(self):
""" Creates a list of br pin names associated with read ports """
br_pins = self.rw_br_names + self.r_br_names
return br_pins
def list_write_bl_names(self):
""" Creates a list of bl pin names associated with write ports """
bl_pins = self.rw_bl_names + self.w_bl_names
return bl_pins
def list_write_br_names(self):
""" Creates a list of br pin names asscociated with write ports"""
br_pins = self.rw_br_names + self.w_br_names
return br_pins
def route_rbc_short(self): def route_rbc_short(self):
""" route the short from Q_bar to gnd necessary for the replica bitcell """ """ route the short from Q_bar to gnd necessary for the replica bitcell """
Q_bar_pos = self.inverter_pmos_right.get_pin("S").center() Q_bar_pos = self.inverter_pmos_right.get_pin("S").center()

View File

@ -73,9 +73,9 @@ class delay(simulation):
debug.error("Given probe_data is not an integer to specify a data bit",1) debug.error("Given probe_data is not an integer to specify a data bit",1)
#Adding port options here which the characterizer cannot handle. Some may be added later like ROM #Adding port options here which the characterizer cannot handle. Some may be added later like ROM
if len(self.read_index) == 0: if len(self.read_ports) == 0:
debug.error("Characterizer does not currently support SRAMs without read ports.",1) debug.error("Characterizer does not currently support SRAMs without read ports.",1)
if len(self.write_index) == 0: if len(self.write_ports) == 0:
debug.error("Characterizer does not currently support SRAMs without write ports.",1) debug.error("Characterizer does not currently support SRAMs without write ports.",1)
def write_generic_stimulus(self): def write_generic_stimulus(self):
@ -89,12 +89,12 @@ class delay(simulation):
self.sf.write("\n* Instantiation of the SRAM\n") self.sf.write("\n* Instantiation of the SRAM\n")
self.stim.inst_sram(sram=self.sram, self.stim.inst_sram(sram=self.sram,
port_signal_names=(self.addr_name,self.din_name,self.dout_name), port_signal_names=(self.addr_name,self.din_name,self.dout_name),
port_info=(self.total_ports,self.write_index,self.read_index), port_info=(len(self.all_ports),self.write_ports,self.read_ports),
abits=self.addr_size, abits=self.addr_size,
dbits=self.word_size, dbits=self.word_size,
sram_name=self.name) sram_name=self.name)
self.sf.write("\n* SRAM output loads\n") self.sf.write("\n* SRAM output loads\n")
for port in self.read_index: for port in self.read_ports:
for i in range(self.word_size): for i in range(self.word_size):
self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load)) self.sf.write("CD{0}{1} {2}{0}_{1} 0 {3}f\n".format(port,i,self.dout_name,self.load))
@ -132,7 +132,7 @@ class delay(simulation):
self.gen_control() self.gen_control()
self.sf.write("\n* Generation of Port clock signal\n") self.sf.write("\n* Generation of Port clock signal\n")
for port in range(self.total_ports): for port in self.all_ports:
self.stim.gen_pulse(sig_name="CLK{0}".format(port), self.stim.gen_pulse(sig_name="CLK{0}".format(port),
v1=0, v1=0,
v2=self.vdd_voltage, v2=self.vdd_voltage,
@ -171,24 +171,24 @@ class delay(simulation):
# generate data and addr signals # generate data and addr signals
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
for write_port in self.write_index: for write_port in self.write_ports:
for i in range(self.word_size): for i in range(self.word_size):
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i), self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i),
v_val=0) v_val=0)
for port in range(self.total_ports): for port in self.all_ports:
for i in range(self.addr_size): for i in range(self.addr_size):
self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i), self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i),
v_val=0) v_val=0)
# generate control signals # generate control signals
self.sf.write("\n* Generation of control signals\n") self.sf.write("\n* Generation of control signals\n")
for port in range(self.total_ports): for port in self.all_ports:
self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage) self.stim.gen_constant(sig_name="CSB{0}".format(port), v_val=self.vdd_voltage)
if port in self.write_index and port in self.read_index: if port in self.readwrite_ports:
self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage) self.stim.gen_constant(sig_name="WEB{0}".format(port), v_val=self.vdd_voltage)
self.sf.write("\n* Generation of global clock signal\n") self.sf.write("\n* Generation of global clock signal\n")
for port in range(self.total_ports): for port in self.all_ports:
self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0) self.stim.gen_constant(sig_name="CLK{0}".format(port), v_val=0)
self.write_power_measures() self.write_power_measures()
@ -317,7 +317,7 @@ class delay(simulation):
double the period until we find a valid period to use as a double the period until we find a valid period to use as a
starting point. starting point.
""" """
debug.check(port in self.read_index, "Characterizer requires a read port to determine a period.") debug.check(port in self.read_ports, "Characterizer requires a read port to determine a period.")
feasible_period = float(tech.spice["feasible_period"]) feasible_period = float(tech.spice["feasible_period"])
time_out = 9 time_out = 9
@ -362,18 +362,18 @@ class delay(simulation):
Loops through all read ports determining the feasible period and collecting Loops through all read ports determining the feasible period and collecting
delay information from each port. delay information from each port.
""" """
feasible_delays = [{} for i in range(self.total_ports)] feasible_delays = [{} for i in self.all_ports]
#Get initial feasible delays from first port #Get initial feasible delays from first port
feasible_delays[self.read_index[0]] = self.find_feasible_period_one_port(self.read_index[0]) feasible_delays[self.read_ports[0]] = self.find_feasible_period_one_port(self.read_ports[0])
previous_period = self.period previous_period = self.period
#Loops through all the ports checks if the feasible period works. Everything restarts it if does not. #Loops through all the ports checks if the feasible period works. Everything restarts it if does not.
#Write ports do not produce delays which is why they are not included here. #Write ports do not produce delays which is why they are not included here.
i = 1 i = 1
while i < len(self.read_index): while i < len(self.read_ports):
port = self.read_index[i] port = self.read_ports[i]
#Only extract port values from the specified port, not the entire results. #Only extract port values from the specified port, not the entire results.
feasible_delays[port].update(self.find_feasible_period_one_port(port)) feasible_delays[port].update(self.find_feasible_period_one_port(port))
#Function sets the period. Restart the entire process if period changes to collect accurate delays #Function sets the period. Restart the entire process if period changes to collect accurate delays
@ -416,7 +416,7 @@ class delay(simulation):
#Sanity Check #Sanity Check
debug.check(self.period > 0, "Target simulation period non-positive") debug.check(self.period > 0, "Target simulation period non-positive")
result = [{} for i in range(self.total_ports)] result = [{} for i in self.all_ports]
# Checking from not data_value to data_value # Checking from not data_value to data_value
self.write_delay_stimulus() self.write_delay_stimulus()
@ -518,7 +518,7 @@ class delay(simulation):
#Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position. #Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position.
#For testing purposes, only checks read ports. #For testing purposes, only checks read ports.
for port in self.read_index: for port in self.read_ports:
target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period) target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period)
#The min period of one port becomes the new lower bound. Reset the upper_bound. #The min period of one port becomes the new lower bound. Reset the upper_bound.
lb_period = target_period lb_period = target_period
@ -683,8 +683,8 @@ class delay(simulation):
"""Simulate all specified output loads and input slews pairs of all ports""" """Simulate all specified output loads and input slews pairs of all ports"""
measure_data = self.get_empty_measure_data_dict() measure_data = self.get_empty_measure_data_dict()
#Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways. #Set the target simulation ports to all available ports. This make sims slower but failed sims exit anyways.
self.targ_read_ports = self.read_index self.targ_read_ports = self.read_ports
self.targ_write_ports = self.write_index self.targ_write_ports = self.write_ports
for slew in slews: for slew in slews:
for load in loads: for load in loads:
self.set_load_slew(load,slew) self.set_load_slew(load,slew)
@ -693,7 +693,7 @@ class delay(simulation):
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load)) debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load))
debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load)) debug.info(1, "Simulation Passed: Port {0} slew={1} load={2}".format("All", self.slew,self.load))
#The results has a dict for every port but dicts can be empty (e.g. ports were not targeted). #The results has a dict for every port but dicts can be empty (e.g. ports were not targeted).
for port in range(self.total_ports): for port in self.all_ports:
for mname,value in delay_results[port].items(): for mname,value in delay_results[port].items():
if "power" in mname: if "power" in mname:
# Subtract partial array leakage and add full array leakage for the power measures # Subtract partial array leakage and add full array leakage for the power measures
@ -763,15 +763,15 @@ class delay(simulation):
def get_available_port(self,get_read_port): def get_available_port(self,get_read_port):
"""Returns the first accessible read or write port. """ """Returns the first accessible read or write port. """
if get_read_port and len(self.read_index) > 0: if get_read_port and len(self.read_ports) > 0:
return self.read_index[0] return self.read_ports[0]
elif not get_read_port and len(self.write_index) > 0: elif not get_read_port and len(self.write_ports) > 0:
return self.write_index[0] return self.write_ports[0]
return None return None
def set_stimulus_variables(self): def set_stimulus_variables(self):
simulation.set_stimulus_variables(self) simulation.set_stimulus_variables(self)
self.measure_cycles = [{} for port in range(self.total_ports)] self.measure_cycles = [{} for port in self.all_ports]
def create_test_cycles(self): def create_test_cycles(self):
"""Returns a list of key time-points [ns] of the waveform (each rising edge) """Returns a list of key time-points [ns] of the waveform (each rising edge)
@ -819,7 +819,7 @@ class delay(simulation):
for load in loads: for load in loads:
self.set_load_slew(load,slew) self.set_load_slew(load,slew)
bank_delay = self.sram.analytical_delay(self.vdd_voltage, self.slew,self.load) bank_delay = self.sram.analytical_delay(self.vdd_voltage, self.slew,self.load)
for port in range(self.total_ports): for port in self.all_ports:
for mname in self.delay_meas_names+self.power_meas_names: for mname in self.delay_meas_names+self.power_meas_names:
if "power" in mname: if "power" in mname:
port_data[port][mname].append(power.dynamic) port_data[port][mname].append(power.dynamic)
@ -877,7 +877,7 @@ class delay(simulation):
def gen_data(self): def gen_data(self):
""" Generates the PWL data inputs for a simulation timing test. """ """ Generates the PWL data inputs for a simulation timing test. """
for write_port in self.write_index: for write_port in self.write_ports:
for i in range(self.word_size): for i in range(self.word_size):
sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i) sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i)
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[write_port][i], self.period, self.slew, 0.05)
@ -887,16 +887,16 @@ class delay(simulation):
Generates the address inputs for a simulation timing test. Generates the address inputs for a simulation timing test.
This alternates between all 1's and all 0's for the address. This alternates between all 1's and all 0's for the address.
""" """
for port in range(self.total_ports): for port in self.all_ports:
for i in range(self.addr_size): for i in range(self.addr_size):
sig_name = "{0}{1}_{2}".format(self.addr_name,port,i) 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) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][i], self.period, self.slew, 0.05)
def gen_control(self): def gen_control(self):
""" Generates the control signals """ """ Generates the control signals """
for port in range(self.total_ports): for port in self.all_ports:
self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("CSB{0}".format(port), self.cycle_times, self.csb_values[port], self.period, self.slew, 0.05)
if port in self.read_index and port in self.write_index: if port in self.readwrite_ports:
self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("WEB{0}".format(port), self.cycle_times, self.web_values[port], self.period, self.slew, 0.05)
@ -904,5 +904,5 @@ class delay(simulation):
"""Make a dict of lists for each type of delay and power measurement to append results to""" """Make a dict of lists for each type of delay and power measurement to append results to"""
measure_names = self.delay_meas_names + self.power_meas_names measure_names = self.delay_meas_names + self.power_meas_names
#Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists. #Create list of dicts. List lengths is # of ports. Each dict maps the measurement names to lists.
measure_data = [{mname:[] for mname in measure_names} for i in range(self.total_ports)] measure_data = [{mname:[] for mname in measure_names} for i in self.all_ports]
return measure_data return measure_data

View File

@ -80,8 +80,8 @@ class functional(simulation):
# Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously. # Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously.
# This will test the viablilty of the transistor sizing in the bitcell. # This will test the viablilty of the transistor sizing in the bitcell.
for port in range(self.total_ports): for port in self.all_ports:
if self.port_id[port] == "w": if port in self.write_ports:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port) self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
else: else:
comment = self.gen_cycle_comment("read", word, addr, port, self.t_current) comment = self.gen_cycle_comment("read", word, addr, port, self.t_current)
@ -94,10 +94,10 @@ class functional(simulation):
# Perform a random sequence of writes and reads on random ports, using random addresses and random words # Perform a random sequence of writes and reads on random ports, using random addresses and random words
for i in range(self.num_cycles): for i in range(self.num_cycles):
w_addrs = [] w_addrs = []
for port in range(self.total_ports): for port in self.all_ports:
if self.port_id[port] == "rw": if port in self.readwrite_ports:
op = random.choice(rw_ops) op = random.choice(rw_ops)
elif self.port_id[port] == "w": elif port in self.write_ports:
op = random.choice(w_ops) op = random.choice(w_ops)
else: else:
op = random.choice(r_ops) op = random.choice(r_ops)
@ -225,17 +225,17 @@ class functional(simulation):
self.sf.write("\n* Instantiation of the SRAM\n") self.sf.write("\n* Instantiation of the SRAM\n")
self.stim.inst_sram(sram=self.sram, self.stim.inst_sram(sram=self.sram,
port_signal_names=(self.addr_name,self.din_name,self.dout_name), port_signal_names=(self.addr_name,self.din_name,self.dout_name),
port_info=(self.total_ports, self.write_index, self.read_index), port_info=(len(self.all_ports), self.write_ports, self.read_ports),
abits=self.addr_size, abits=self.addr_size,
dbits=self.word_size, dbits=self.word_size,
sram_name=self.name) sram_name=self.name)
# Add load capacitance to each of the read ports # Add load capacitance to each of the read ports
self.sf.write("\n* SRAM output loads\n") self.sf.write("\n* SRAM output loads\n")
for port in range(self.total_read): for port in self.read_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
sig_name="{0}{1}_{2} ".format(self.dout_name, self.read_index[port], bit) sig_name="{0}{1}_{2} ".format(self.dout_name, port, bit)
self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(self.read_index[port], bit, sig_name, self.load)) self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(port, bit, sig_name, self.load))
# Write debug comments to stim file # Write debug comments to stim file
self.sf.write("\n\n * Sequence of operations\n") self.sf.write("\n\n * Sequence of operations\n")
@ -244,27 +244,27 @@ class functional(simulation):
# Generate data input bits # Generate data input bits
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
for port in range(self.total_write): for port in self.write_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
sig_name="{0}{1}_{2} ".format(self.din_name, port, bit) sig_name="{0}{1}_{2} ".format(self.din_name, port, bit)
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05) self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[port][bit], self.period, self.slew, 0.05)
# Generate address bits # Generate address bits
for port in range(self.total_ports): for port in self.all_ports:
for bit in range(self.addr_size): for bit in range(self.addr_size):
sig_name="{0}{1}_{2} ".format(self.addr_name, port, bit) 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) self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05)
# Generate control signals # Generate control signals
self.sf.write("\n * Generation of control signals\n") self.sf.write("\n * Generation of control signals\n")
for port in range(self.total_ports): for port in self.all_ports:
self.stim.gen_pwl("CSB{}".format(port), self.cycle_times , self.csb_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("CSB{}".format(port), self.cycle_times , self.csb_values[port], self.period, self.slew, 0.05)
for port in range(self.num_rw_ports): for port in self.readwrite_ports:
self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05) self.stim.gen_pwl("WEB{}".format(port), self.cycle_times , self.web_values[port], self.period, self.slew, 0.05)
# Generate CLK signals # Generate CLK signals
for port in range(self.total_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(tech.spice["clk"], port),
v1=self.gnd_voltage, v1=self.gnd_voltage,
v2=self.vdd_voltage, v2=self.vdd_voltage,

View File

@ -27,9 +27,11 @@ class lib:
def set_port_indices(self): def set_port_indices(self):
"""Copies port information set in the SRAM instance""" """Copies port information set in the SRAM instance"""
self.total_port_num = self.sram.total_ports self.total_port_num = len(self.sram.all_ports)
self.read_ports = self.sram.read_index self.all_ports = self.sram.all_ports
self.write_ports = self.sram.write_index self.readwrite_ports = self.sram.readwrite_ports
self.read_ports = self.sram.read_ports
self.write_ports = self.sram.write_ports
def prepare_tables(self): def prepare_tables(self):
""" Determine the load/slews if they aren't specified in the config file. """ """ Determine the load/slews if they aren't specified in the config file. """
@ -93,7 +95,7 @@ class lib:
self.write_header() self.write_header()
#Loop over all ports. #Loop over all ports.
for port in range(self.total_port_num): for port in self.all_ports:
#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)
@ -387,7 +389,7 @@ class lib:
""" Adds control pins timing results.""" """ Adds control pins timing results."""
#The control pins are still to be determined. This is a placeholder for what could be. #The control pins are still to be determined. This is a placeholder for what could be.
ctrl_pin_names = ["CSb{0}".format(port)] ctrl_pin_names = ["CSb{0}".format(port)]
if port in self.write_ports and port in self.read_ports: if port in self.readwrite_ports:
ctrl_pin_names.append("WEb{0}".format(port)) ctrl_pin_names.append("WEb{0}".format(port))
for i in ctrl_pin_names: for i in ctrl_pin_names:

View File

@ -22,13 +22,10 @@ class simulation():
self.num_banks = self.sram.num_banks self.num_banks = self.sram.num_banks
self.sp_file = spfile self.sp_file = spfile
self.total_ports = self.sram.total_ports self.all_ports = self.sram.all_ports
self.total_write = self.sram.total_write self.readwrite_ports = self.sram.readwrite_ports
self.total_read = self.sram.total_read self.read_ports = self.sram.read_ports
self.read_index = self.sram.read_index self.write_ports = self.sram.write_ports
self.write_index = self.sram.write_index
self.num_rw_ports = self.sram.num_rw_ports
self.port_id = self.sram.port_id
def set_corner(self,corner): def set_corner(self,corner):
""" Set the corner values """ """ Set the corner values """
@ -48,12 +45,12 @@ class simulation():
self.t_current = 0 self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port # control signals: only one cs_b for entire multiported sram, one we_b for each write port
self.csb_values = [[] for port in range(self.total_ports)] self.csb_values = [[] for port in self.all_ports]
self.web_values = [[] for port in range(self.num_rw_ports)] self.web_values = [[] for port in self.readwrite_ports]
# Three dimensional list to handle each addr and data bits for wach port over the number of checks # Three dimensional list to handle each addr and data bits for wach port over the number of checks
self.addr_values = [[[] for bit in range(self.addr_size)] for port in range(self.total_ports)] self.addr_values = [[[] for bit in range(self.addr_size)] for port in self.all_ports]
self.data_values = [[[] for bit in range(self.word_size)] for port in range(self.total_write)] self.data_values = [[[] for bit in range(self.word_size)] for port in self.write_ports]
# For generating comments in SPICE stimulus # For generating comments in SPICE stimulus
self.cycle_comments = [] self.cycle_comments = []
@ -75,7 +72,7 @@ class simulation():
# Append the values depending on the type of port # Append the values depending on the type of port
self.csb_values[port].append(csb_val) self.csb_values[port].append(csb_val)
# If port is in both lists, add rw control signal. Condition indicates its a RW port. # If port is in both lists, add rw control signal. Condition indicates its a RW port.
if port < self.num_rw_ports: if port in self.readwrite_ports:
self.web_values[port].append(web_val) self.web_values[port].append(web_val)
def add_data(self, data, port): def add_data(self, data, port):
@ -108,7 +105,7 @@ class simulation():
def add_write(self, comment, address, data, port): def add_write(self, comment, address, data, port):
""" Add the control values for a write cycle. """ """ Add the control values for a write cycle. """
debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index)) debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports))
debug.info(2, comment) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
self.append_cycle_comment(port, comment) self.append_cycle_comment(port, comment)
@ -123,13 +120,13 @@ class simulation():
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
noop_data = "0"*self.word_size noop_data = "0"*self.word_size
#Add noops to all other ports. #Add noops to all other ports.
for unselected_port in range(self.total_ports): for unselected_port in self.all_ports:
if unselected_port != port: if unselected_port != port:
self.add_noop_one_port(address, noop_data, unselected_port) self.add_noop_one_port(address, noop_data, unselected_port)
def add_read(self, comment, address, din_data, port): def add_read(self, comment, address, din_data, port):
""" Add the control values for a read cycle. """ """ Add the control values for a read cycle. """
debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index)) debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports))
debug.info(2, comment) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
self.append_cycle_comment(port, comment) self.append_cycle_comment(port, comment)
@ -139,14 +136,14 @@ class simulation():
self.add_control_one_port(port, "read") self.add_control_one_port(port, "read")
#If the port is also a readwrite then add data. #If the port is also a readwrite then add data.
if port in self.write_index: if port in self.write_ports:
self.add_data(din_data,port) self.add_data(din_data,port)
self.add_address(address, port) self.add_address(address, port)
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port #This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
noop_data = "0"*self.word_size noop_data = "0"*self.word_size
#Add noops to all other ports. #Add noops to all other ports.
for unselected_port in range(self.total_ports): for unselected_port in self.all_ports:
if unselected_port != port: if unselected_port != port:
self.add_noop_one_port(address, noop_data, unselected_port) self.add_noop_one_port(address, noop_data, unselected_port)
@ -159,12 +156,12 @@ class simulation():
self.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
for port in range(self.total_ports): for port in self.all_ports:
self.add_noop_one_port(address, data, port) self.add_noop_one_port(address, data, port)
def add_write_one_port(self, comment, address, data, port): def add_write_one_port(self, comment, address, data, port):
""" Add the control values for a write cycle. Does not increment the period. """ """ Add the control values for a write cycle. Does not increment the period. """
debug.check(port in self.write_index, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_index)) debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports))
debug.info(2, comment) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
@ -174,20 +171,20 @@ class simulation():
def add_read_one_port(self, comment, address, din_data, port): def add_read_one_port(self, comment, address, din_data, port):
""" Add the control values for a read cycle. Does not increment the period. """ """ Add the control values for a read cycle. Does not increment the period. """
debug.check(port in self.read_index, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_index)) debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports))
debug.info(2, comment) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
self.add_control_one_port(port, "read") self.add_control_one_port(port, "read")
#If the port is also a readwrite then add data. #If the port is also a readwrite then add data.
if port in self.write_index: if port in self.write_ports:
self.add_data(din_data,port) self.add_data(din_data,port)
self.add_address(address, port) self.add_address(address, port)
def add_noop_one_port(self, address, data, port): def add_noop_one_port(self, address, data, port):
""" Add the control values for a noop to a single port. Does not increment the period. """ """ Add the control values for a noop to a single port. Does not increment the period. """
self.add_control_one_port(port, "noop") self.add_control_one_port(port, "noop")
if port in self.write_index: if port in self.write_ports:
self.add_data(data,port) self.add_data(data,port)
self.add_address(address, port) self.add_address(address, port)

View File

@ -5,6 +5,7 @@ import design
import math import math
from math import log,sqrt,ceil from math import log,sqrt,ceil
import contact import contact
import pgates
from pinv import pinv from pinv import pinv
from pnand2 import pnand2 from pnand2 import pnand2
from pnor2 import pnor2 from pnor2 import pnor2
@ -66,26 +67,26 @@ class bank(design.design):
def add_pins(self): def add_pins(self):
""" Adding pins for Bank module""" """ Adding pins for Bank module"""
for port in range(self.total_read): for port in self.read_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.add_pin("dout{0}_{1}".format(self.read_index[port],bit),"OUT") self.add_pin("dout{0}_{1}".format(port,bit),"OUT")
for port in range(self.total_write): for port in self.write_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.add_pin("din{0}_{1}".format(port,bit),"IN") self.add_pin("din{0}_{1}".format(port,bit),"IN")
for port in range(self.total_ports): for port in self.all_ports:
for bit in range(self.addr_size): for bit in range(self.addr_size):
self.add_pin("addr{0}_{1}".format(port,bit),"INPUT") self.add_pin("addr{0}_{1}".format(port,bit),"INPUT")
# For more than one bank, we have a bank select and name # For more than one bank, we have a bank select and name
# the signals gated_*. # the signals gated_*.
if self.num_banks > 1: if self.num_banks > 1:
for port in range(self.total_ports): for port in self.all_ports:
self.add_pin("bank_sel{}".format(port),"INPUT") self.add_pin("bank_sel{}".format(port),"INPUT")
for port in range(self.total_read): for port in self.read_ports:
self.add_pin("s_en{0}".format(self.read_index[port]), "INPUT") self.add_pin("s_en{0}".format(port), "INPUT")
for port in range(self.total_write): for port in self.write_ports:
self.add_pin("w_en{0}".format(port), "INPUT") self.add_pin("w_en{0}".format(port), "INPUT")
for port in range(self.total_ports): for port in self.all_ports:
self.add_pin("clk_buf_bar{0}".format(port),"INPUT") self.add_pin("clk_buf_bar{0}".format(port),"INPUT")
self.add_pin("clk_buf{0}".format(port),"INPUT") self.add_pin("clk_buf{0}".format(port),"INPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd","POWER")
@ -96,8 +97,9 @@ class bank(design.design):
""" Create routing amoung the modules """ """ Create routing amoung the modules """
self.route_central_bus() self.route_central_bus()
self.route_precharge_to_bitcell_array() self.route_precharge_to_bitcell_array()
self.route_col_mux_to_bitcell_array() self.route_col_mux_to_precharge_array()
self.route_sense_amp_to_col_mux_or_bitcell_array() self.route_sense_amp_to_col_mux_or_precharge_array()
self.route_write_driver_to_sense_amp()
self.route_sense_amp_out() self.route_sense_amp_out()
self.route_wordline_driver() self.route_wordline_driver()
self.route_write_driver() self.route_write_driver()
@ -128,25 +130,75 @@ class bank(design.design):
self.create_bank_select() self.create_bank_select()
def compute_module_offsets(self):
"""
Compute the module offsets.
"""
# UPPER RIGHT QUADRANT
# Bitcell array is placed at (0,0)
self.bitcell_array_offset = vector(0,0)
# LOWER RIGHT QUADRANT
# Below the bitcell array
y_offset = self.precharge_array[0].height + self.m2_gap
self.precharge_offset = vector(0,-y_offset)
if self.col_addr_size > 0:
y_offset += self.column_mux_array[0].height + self.m2_gap
self.column_mux_offset = vector(0,-y_offset)
y_offset += self.sense_amp_array.height + self.m2_gap
self.sense_amp_offset = vector(0,-y_offset)
y_offset += self.write_driver_array.height + self.m2_gap
self.write_driver_offset = vector(0,-y_offset)
# UPPER LEFT QUADRANT
# To the left of the bitcell array
# The wordline driver is placed to the right of the main decoder width.
x_offset = self.central_bus_width + self.wordline_driver.width - self.m2_pitch
self.wordline_driver_offset = vector(-x_offset,0)
x_offset += self.row_decoder.width + self.m2_pitch
self.row_decoder_offset = vector(-x_offset,0)
# LOWER LEFT QUADRANT
# Place the col decoder right aligned with row decoder (x_offset doesn't change)
# Below the bitcell array
if self.col_addr_size > 0:
y_offset = self.col_decoder.height
else:
y_offset = 0
y_offset += 2*drc("well_to_well")
self.column_decoder_offset = vector(-x_offset,-y_offset)
# Bank select gets placed below the column decoder (x_offset doesn't change)
if self.col_addr_size > 0:
y_offset = min(self.column_decoder_offset.y, self.column_mux_offset.y)
else:
y_offset = self.row_decoder_offset.y
if self.num_banks > 1:
y_offset += self.bank_select.height + drc("well_to_well")
self.bank_select_offset = vector(-x_offset,-y_offset)
def place_modules(self): def place_modules(self):
""" Add modules. The order should not matter! """ """ Place the modules. """
# Above the bitcell array self.compute_module_offsets()
self.place_bitcell_array()
self.place_precharge_array()
# Below the bitcell array # UPPER RIGHT QUADRANT
self.place_column_mux_array() self.place_bitcell_array(self.bitcell_array_offset)
self.place_sense_amp_array()
self.place_write_driver_array()
# To the left of the bitcell array # LOWER RIGHT QUADRANT
self.place_row_decoder() self.place_precharge_array([self.precharge_offset]*len(self.read_ports))
self.place_wordline_driver() self.place_column_mux_array([self.column_mux_offset]*len(self.all_ports))
self.place_column_decoder() self.place_sense_amp_array([self.sense_amp_offset]*len(self.read_ports))
self.place_write_driver_array([self.write_driver_offset]*len(self.write_ports))
self.place_bank_select() # UPPER LEFT QUADRANT
self.place_row_decoder([self.row_decoder_offset]*len(self.all_ports))
self.place_wordline_driver([self.wordline_driver_offset]*len(self.all_ports))
# LOWER LEFT QUADRANT
self.place_column_decoder([self.column_decoder_offset]*len(self.all_ports))
self.place_bank_select([self.bank_select_offset]*len(self.all_ports))
def compute_sizes(self): def compute_sizes(self):
@ -184,7 +236,7 @@ class bank(design.design):
# These will be outputs of the gaters if this is multibank, if not, normal signals. # These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = [] self.control_signals = []
for port in range(self.total_ports): for port in self.all_ports:
if self.num_banks > 1: if self.num_banks > 1:
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]]) self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
else: else:
@ -201,7 +253,7 @@ class bank(design.design):
# A space for wells or jogging m2 # A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"), self.m2_gap = max(2*drc("pwell_to_nwell") + drc("well_enclosure_active"),
2*self.m2_pitch) 3*self.m2_pitch)
def add_modules(self): def add_modules(self):
@ -226,30 +278,27 @@ class bank(design.design):
self.add_mod(self.bitcell_array) self.add_mod(self.bitcell_array)
# create arrays of bitline and bitline_bar names for read, write, or all ports # create arrays of bitline and bitline_bar names for read, write, or all ports
self.read_bl_list = self.bitcell.list_read_bl_names() self.bl_names = self.bitcell.list_all_bl_names()
self.read_br_list = self.bitcell.list_read_br_names() self.br_names = self.bitcell.list_all_br_names()
self.write_bl_list = self.bitcell.list_write_bl_names() self.wl_names = self.bitcell.list_all_wl_names()
self.write_br_list = self.bitcell.list_write_br_names() self.bitline_names = self.bitcell.list_all_bitline_names()
self.total_bl_list = self.bitcell.list_all_bl_names()
self.total_br_list = self.bitcell.list_all_br_names()
self.total_wl_list = self.bitcell.list_all_wl_names()
self.total_bitline_list = self.bitcell.list_all_bitline_names()
self.precharge_array = [] self.precharge_array = []
for port in range(self.total_read): for port in self.all_ports:
self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.read_bl_list[port], bitcell_br=self.read_br_list[port])) if port in self.read_ports:
self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.bl_names[port], bitcell_br=self.br_names[port]))
self.add_mod(self.precharge_array[port]) self.add_mod(self.precharge_array[port])
else:
self.precharge_array.append(None)
if self.col_addr_size > 0: if self.col_addr_size > 0:
self.column_mux_array = [] self.column_mux_array = []
for port in range(self.total_ports): for port in self.all_ports:
self.column_mux_array.append(self.mod_column_mux_array(columns=self.num_cols, self.column_mux_array.append(self.mod_column_mux_array(columns=self.num_cols,
word_size=self.word_size, word_size=self.word_size,
bitcell_bl=self.total_bl_list[port], bitcell_bl=self.bl_names[port],
bitcell_br=self.total_br_list[port])) bitcell_br=self.br_names[port]))
self.add_mod(self.column_mux_array[port]) self.add_mod(self.column_mux_array[port])
@ -284,151 +333,154 @@ class bank(design.design):
temp = [] temp = []
for col in range(self.num_cols): for col in range(self.num_cols):
for bitline in self.total_bitline_list: for bitline in self.bitline_names:
temp.append(bitline+"_{0}".format(col)) temp.append(bitline+"_{0}".format(col))
for row in range(self.num_rows): for row in range(self.num_rows):
for wordline in self.total_wl_list: for wordline in self.wl_names:
temp.append(wordline+"_{0}".format(row)) temp.append(wordline+"_{0}".format(row))
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_bitcell_array(self): def place_bitcell_array(self, offset):
""" Placing Bitcell Array """ """ Placing Bitcell Array """
self.bitcell_array_inst.place(vector(0,0)) self.bitcell_array_inst.place(offset)
def create_precharge_array(self): def create_precharge_array(self):
""" Creating Precharge """ """ Creating Precharge """
self.precharge_array_inst = [] self.precharge_array_inst = []
for port in range(self.total_read): for port in self.read_ports:
self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port), self.precharge_array_inst.append(self.add_inst(name="precharge_array{}".format(port),
mod=self.precharge_array[port])) mod=self.precharge_array[port]))
temp = [] temp = []
for i in range(self.num_cols): for i in range(self.num_cols):
temp.append(self.read_bl_list[port]+"_{0}".format(i)) temp.append(self.bl_names[port]+"_{0}".format(i))
temp.append(self.read_br_list[port]+"_{0}".format(i)) temp.append(self.br_names[port]+"_{0}".format(i))
temp.extend([self.prefix+"clk_buf_bar{0}".format(self.read_index[port]), "vdd"]) temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_precharge_array(self): def place_precharge_array(self, offsets):
""" Placing Precharge """ """ Placing Precharge """
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.")
# FIXME: place for multiport # FIXME: place for multiport
for port in range(self.total_read): for port in self.read_ports:
# The wells must be far enough apart self.precharge_array_inst[port].place(offsets[port])
# The enclosure is for the well and the spacing is to the bitcell wells
y_offset = self.bitcell_array.height + self.m2_gap
self.precharge_array_inst[port].place(vector(0,y_offset))
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.col_mux_array_inst = []
if self.col_addr_size == 0: if self.col_addr_size == 0:
return return
self.col_mux_array_inst = [] for port in self.all_ports:
for port in range(self.total_ports):
self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port), self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port),
mod=self.column_mux_array[port])) mod=self.column_mux_array[port]))
temp = [] temp = []
for col in range(self.num_cols): for col in range(self.num_cols):
temp.append(self.total_bl_list[port]+"_{0}".format(col)) temp.append(self.bl_names[port]+"_{0}".format(col))
temp.append(self.total_br_list[port]+"_{0}".format(col)) temp.append(self.br_names[port]+"_{0}".format(col))
for word in range(self.words_per_row): for word in range(self.words_per_row):
temp.append("sel{0}_{1}".format(port,word)) temp.append("sel{0}_{1}".format(port,word))
for bit in range(self.word_size): for bit in range(self.word_size):
temp.append(self.total_bl_list[port]+"_out_{0}".format(bit)) temp.append(self.bl_names[port]+"_out_{0}".format(bit))
temp.append(self.total_br_list[port]+"_out_{0}".format(bit)) temp.append(self.br_names[port]+"_out_{0}".format(bit))
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_column_mux_array(self):
def place_column_mux_array(self, offsets):
""" 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:
self.column_mux_height = self.column_mux_array[0].height + self.m2_gap
else:
self.column_mux_height = 0
return return
for port in range(self.total_ports): debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.")
y_offset = self.column_mux_height
self.col_mux_array_inst[port].place(vector(0,y_offset).scale(-1,-1)) for port in self.all_ports:
self.col_mux_array_inst[port].place(offsets[port])
def create_sense_amp_array(self): def create_sense_amp_array(self):
""" Creating Sense amp """ """ Creating Sense amp """
self.sense_amp_array_inst = [] self.sense_amp_array_inst = []
for port in range(self.total_read): for port in self.read_ports:
self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port), self.sense_amp_array_inst.append(self.add_inst(name="sense_amp_array{}".format(port),
mod=self.sense_amp_array)) mod=self.sense_amp_array))
temp = [] temp = []
for bit in range(self.word_size): for bit in range(self.word_size):
temp.append("dout{0}_{1}".format(self.read_index[port],bit)) temp.append("dout{0}_{1}".format(port,bit))
if self.words_per_row == 1: if self.words_per_row == 1:
temp.append(self.read_bl_list[port]+"_{0}".format(bit)) temp.append(self.bl_names[port]+"_{0}".format(bit))
temp.append(self.read_br_list[port]+"_{0}".format(bit)) temp.append(self.br_names[port]+"_{0}".format(bit))
else: else:
temp.append(self.read_bl_list[port]+"_out_{0}".format(bit)) temp.append(self.bl_names[port]+"_out_{0}".format(bit))
temp.append(self.read_br_list[port]+"_out_{0}".format(bit)) temp.append(self.br_names[port]+"_out_{0}".format(bit))
temp.extend([self.prefix+"s_en{}".format(self.read_index[port]), "vdd", "gnd"]) temp.extend([self.prefix+"s_en{}".format(port), "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_sense_amp_array(self): def place_sense_amp_array(self, offsets):
""" Placing Sense amp """ """ Placing Sense amp """
debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.")
# FIXME: place for multiport # FIXME: place for multiport
for port in range(self.total_read): for port in self.read_ports:
y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap self.sense_amp_array_inst[port].place(offsets[port])
self.sense_amp_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
def create_write_driver_array(self): def create_write_driver_array(self):
""" Creating Write Driver """ """ Creating Write Driver """
self.write_driver_array_inst = [] self.write_driver_array_inst = []
for port in range(self.total_write): for port in self.all_ports:
if port in self.write_ports:
self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port), self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port),
mod=self.write_driver_array)) mod=self.write_driver_array))
else:
self.write_driver_array_inst.append(None)
continue
temp = [] temp = []
for bit in range(self.word_size): for bit in range(self.word_size):
temp.append("din{0}_{1}".format(port,bit)) temp.append("din{0}_{1}".format(port,bit))
for bit in range(self.word_size): for bit in range(self.word_size):
if (self.words_per_row == 1): if (self.words_per_row == 1):
temp.append(self.write_bl_list[port]+"_{0}".format(bit)) temp.append(self.bl_names[port]+"_{0}".format(bit))
temp.append(self.write_br_list[port]+"_{0}".format(bit)) temp.append(self.br_names[port]+"_{0}".format(bit))
else: else:
temp.append(self.write_bl_list[port]+"_out_{0}".format(bit)) temp.append(self.bl_names[port]+"_out_{0}".format(bit))
temp.append(self.write_br_list[port]+"_out_{0}".format(bit)) temp.append(self.br_names[port]+"_out_{0}".format(bit))
temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"]) temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def place_write_driver_array(self): def place_write_driver_array(self, offsets):
""" Placing Write Driver """ """ Placing Write Driver """
# FIXME: place for multiport debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.")
for port in range(self.total_write):
y_offset = self.sense_amp_array.height + self.column_mux_height \ for port in self.write_ports:
+ self.m2_gap + self.write_driver_array.height self.write_driver_array_inst[port].place(offsets[port])
self.write_driver_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
def create_row_decoder(self): def create_row_decoder(self):
""" Create the hierarchical row decoder """ """ Create the hierarchical row decoder """
self.row_decoder_inst = [] self.row_decoder_inst = []
for port in range(self.total_ports): for port in self.all_ports:
self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port), self.row_decoder_inst.append(self.add_inst(name="row_decoder{}".format(port),
mod=self.row_decoder)) mod=self.row_decoder))
@ -441,9 +493,11 @@ class bank(design.design):
self.connect_inst(temp) self.connect_inst(temp)
def place_row_decoder(self): def place_row_decoder(self, offsets):
""" Place the hierarchical row decoder """ """ Place the hierarchical row decoder """
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place row decoder array.")
# The address and control bus will be in between decoder and the main memory array # The address and control bus will be in between decoder and the main memory array
# This bus will route address bits to the decoder input and column mux inputs. # This bus will route address bits to the decoder input and column mux inputs.
# The wires are actually routed after we placed the stuff on both sides. # The wires are actually routed after we placed the stuff on both sides.
@ -451,16 +505,15 @@ class bank(design.design):
# The address flop and decoder are aligned in the x coord. # The address flop and decoder are aligned in the x coord.
# FIXME: place for multiport # FIXME: place for multiport
for port in range(self.total_ports): for port in self.all_ports:
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) self.row_decoder_inst[port].place(offsets[port])
self.row_decoder_inst[port].place(vector(x_offset,0))
def create_wordline_driver(self): def create_wordline_driver(self):
""" Create the Wordline Driver """ """ Create the Wordline Driver """
self.wordline_driver_inst = [] self.wordline_driver_inst = []
for port in range(self.total_ports): for port in self.all_ports:
self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port), self.wordline_driver_inst.append(self.add_inst(name="wordline_driver{}".format(port),
mod=self.wordline_driver)) mod=self.wordline_driver))
@ -468,21 +521,20 @@ class bank(design.design):
for row in range(self.num_rows): for row in range(self.num_rows):
temp.append("dec_out{0}_{1}".format(port,row)) temp.append("dec_out{0}_{1}".format(port,row))
for row in range(self.num_rows): for row in range(self.num_rows):
temp.append(self.total_wl_list[port]+"_{0}".format(row)) temp.append(self.wl_names[port]+"_{0}".format(row))
temp.append(self.prefix+"clk_buf{0}".format(port)) temp.append(self.prefix+"clk_buf{0}".format(port))
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_wordline_driver(self): def place_wordline_driver(self, offsets):
""" Place the Wordline Driver """ """ Place the Wordline Driver """
# FIXME: place for multiport debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.")
for port in range(self.total_ports):
# The wordline driver is placed to the right of the main decoder width. for port in self.all_ports:
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch self.wordline_driver_inst[port].place(offsets[port])
self.wordline_driver_inst[port].place(vector(x_offset,0))
def create_column_decoder(self): def create_column_decoder(self):
@ -504,7 +556,7 @@ class bank(design.design):
debug.error("Invalid column decoder?",-1) debug.error("Invalid column decoder?",-1)
self.col_decoder_inst = [] self.col_decoder_inst = []
for port in range(self.total_ports): for port in self.all_ports:
self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(port), self.col_decoder_inst.append(self.add_inst(name="col_address_decoder{}".format(port),
mod=self.col_decoder)) mod=self.col_decoder))
@ -517,21 +569,17 @@ class bank(design.design):
self.connect_inst(temp) self.connect_inst(temp)
def place_column_decoder(self): def place_column_decoder(self, offsets):
""" """
Place a 2:4 or 3:8 column address decoder. Place a 2:4 or 3:8 column address decoder.
""" """
if self.col_addr_size == 0: if self.col_addr_size == 0:
return return
# FIXME: place for multiport debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.")
for port in range(self.total_ports):
col_decoder_inst = self.col_decoder_inst[port]
# Place the col decoder right aligned with row decoder for port in self.all_ports:
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width) self.col_decoder_inst[port].place(offsets[port])
y_off = -(self.col_decoder.height + 2*drc("well_to_well"))
col_decoder_inst.place(vector(x_off,y_off))
@ -542,7 +590,7 @@ class bank(design.design):
return return
self.bank_select_inst = [] self.bank_select_inst = []
for port in range(self.total_ports): for port in self.all_ports:
self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port), self.bank_select_inst.append(self.add_inst(name="bank_select{}".format(port),
mod=self.bank_select)) mod=self.bank_select))
@ -554,22 +602,16 @@ class bank(design.design):
self.connect_inst(temp) self.connect_inst(temp)
def place_bank_select(self): def place_bank_select(self, offsets):
""" Place the bank select logic. """ """ Place the bank select logic. """
if not self.num_banks > 1: if not self.num_banks > 1:
return return
# FIXME: place for multiport debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.")
for port in range(self.total_ports):
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width) for port in self.all_ports:
if self.col_addr_size > 0: self.bank_select_inst[port].place(offsets[port])
y_off = min(self.col_decoder_inst[port].by(), self.col_mux_array_inst[port].by())
else:
y_off = self.row_decoder_inst[port].by()
y_off -= (self.bank_select.height + drc("well_to_well"))
self.bank_select_pos = vector(x_off,y_off)
self.bank_select_inst[port].place(self.bank_select_pos)
def route_supplies(self): def route_supplies(self):
@ -581,7 +623,7 @@ class bank(design.design):
def route_bank_select(self): def route_bank_select(self):
""" Route the bank select logic. """ """ Route the bank select logic. """
for port in range(self.total_ports): for port in self.all_ports:
if self.port_id[port] == "rw": if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"] bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"] gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
@ -641,7 +683,7 @@ class bank(design.design):
# The max point is always the top of the precharge bitlines # The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array # Add a vdd and gnd power rail above the array
# FIXME: Update multiport # FIXME: Update multiport
self.max_y_offset = self.precharge_array_inst[0].uy() + 3*self.m1_width self.max_y_offset = self.bitcell_array_inst.ur().y + 3*self.m1_width
self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width self.max_x_offset = self.bitcell_array_inst.ur().x + 3*self.m1_width
self.min_x_offset = self.row_decoder_inst[0].lx() self.min_x_offset = self.row_decoder_inst[0].lx()
@ -661,7 +703,7 @@ class bank(design.design):
# and control lines. # and control lines.
# The bank is at (0,0), so this is to the left of the y-axis. # The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs # 2 pitches on the right for vias/jogs to access the inputs
for port in range(self.total_ports): for port in self.all_ports:
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset) control_bus_offset = vector(-self.m2_pitch * self.num_control_lines - self.m2_width, self.min_y_offset)
control_bus_length = self.max_y_offset - self.min_y_offset control_bus_length = self.max_y_offset - self.min_y_offset
self.bus_xoffset = self.create_bus(layer="metal2", self.bus_xoffset = self.create_bus(layer="metal2",
@ -677,12 +719,12 @@ class bank(design.design):
""" Routing of BL and BR between pre-charge and bitcell array """ """ Routing of BL and BR between pre-charge and bitcell array """
# FIXME: Update for multiport # FIXME: Update for multiport
for port in range(self.total_read): for port in self.read_ports:
for col in range(self.num_cols): for col in range(self.num_cols):
precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).bc() precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).uc()
precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).bc() precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).uc()
bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"_{}".format(col)).uc() bitcell_bl = self.bitcell_array_inst.get_pin(self.bl_names[port]+"_{}".format(col)).bc()
bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"_{}".format(col)).uc() bitcell_br = self.bitcell_array_inst.get_pin(self.br_names[port]+"_{}".format(col)).bc()
yoffset = 0.5*(precharge_bl.y+bitcell_bl.y) yoffset = 0.5*(precharge_bl.y+bitcell_bl.y)
self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset), self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset),
@ -691,61 +733,59 @@ class bank(design.design):
vector(bitcell_br.x,yoffset), bitcell_br]) vector(bitcell_br.x,yoffset), bitcell_br])
def route_col_mux_to_bitcell_array(self): def route_col_mux_to_precharge_array(self):
""" Routing of BL and BR between col mux and bitcell array """ """ Routing of BL and BR between col mux and precharge array """
# Only do this if we have a column mux! # Only do this if we have a column mux!
if self.col_addr_size==0: if self.col_addr_size==0:
return return
# FIXME: Update for multiport # FIXME: Update for multiport
for port in range(self.total_ports): for port in self.all_ports:
for col in range(self.num_cols): bottom_inst = self.col_mux_array_inst[port]
col_mux_bl = self.col_mux_array_inst[port].get_pin("bl_{}".format(col)).uc() top_inst = self.precharge_array_inst[port]
col_mux_br = self.col_mux_array_inst[port].get_pin("br_{}".format(col)).uc() self.connect_bitlines(top_inst, bottom_inst, self.num_cols)
bitcell_bl = self.bitcell_array_inst.get_pin(self.total_bl_list[port]+"_{}".format(col)).bc()
bitcell_br = self.bitcell_array_inst.get_pin(self.total_br_list[port]+"_{}".format(col)).bc()
yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y)
self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset),
vector(bitcell_bl.x,yoffset), bitcell_bl])
self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br])
def route_sense_amp_to_col_mux_or_bitcell_array(self):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
for port in range(self.total_read): def route_sense_amp_to_col_mux_or_precharge_array(self):
for bit in range(self.word_size): """ Routing of BL and BR between sense_amp and column mux or precharge array """
sense_amp_bl = self.sense_amp_array_inst[port].get_pin("bl_{}".format(bit)).uc()
sense_amp_br = self.sense_amp_array_inst[port].get_pin("br_{}".format(bit)).uc() for port in self.read_ports:
bottom_inst = self.sense_amp_array_inst[port]
if self.col_addr_size>0: if self.col_addr_size>0:
# Sense amp is connected to the col mux # Sense amp is connected to the col mux
connect_bl = self.col_mux_array_inst[port].get_pin("bl_out_{}".format(bit)).bc() top_inst = self.col_mux_array_inst[port]
connect_br = self.col_mux_array_inst[port].get_pin("br_out_{}".format(bit)).bc() top_bl = "bl_out_{}"
top_br = "br_out_{}"
else: else:
# Sense amp is directly connected to the bitcell array # Sense amp is directly connected to the precharge array
connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"_{}".format(bit)).bc() top_inst = self.precharge_array_inst[port]
connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"_{}".format(bit)).bc() top_bl = "bl_{}"
top_br = "br_{}"
self.connect_bitlines(top_inst, bottom_inst, self.word_size,
top_bl_name=top_bl, top_br_name=top_br)
def route_write_driver_to_sense_amp(self):
""" Routing of BL and BR between write driver and sense amp """
for port in self.write_ports:
bottom_inst = self.write_driver_array_inst[port]
top_inst = self.sense_amp_array_inst[port]
self.connect_bitlines(top_inst, bottom_inst, self.word_size)
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
self.add_path("metal2",[sense_amp_bl, vector(sense_amp_bl.x,yoffset),
vector(connect_bl.x,yoffset), connect_bl])
self.add_path("metal2",[sense_amp_br, vector(sense_amp_br.x,yoffset),
vector(connect_br.x,yoffset), connect_br])
def route_sense_amp_out(self): def route_sense_amp_out(self):
""" Add pins for the sense amp output """ """ Add pins for the sense amp output """
# FIXME: Update for multiport # FIXME: Update for multiport
for port in range(self.total_read): for port in self.read_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit)) data_pin = self.sense_amp_array_inst[port].get_pin("data_{}".format(bit))
self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_index[port],bit), self.add_layout_pin_rect_center(text="dout{0}_{1}".format(self.read_ports[port],bit),
layer=data_pin.layer, layer=data_pin.layer,
offset=data_pin.center(), offset=data_pin.center(),
height=data_pin.height(), height=data_pin.height(),
@ -757,7 +797,7 @@ class bank(design.design):
# FIXME: Update for multiport # FIXME: Update for multiport
# Create inputs for the row address lines # Create inputs for the row address lines
for port in range(self.total_ports): for port in self.all_ports:
for row in range(self.row_addr_size): for row in range(self.row_addr_size):
addr_idx = row + self.col_addr_size addr_idx = row + self.col_addr_size
decoder_name = "addr_{}".format(row) decoder_name = "addr_{}".format(row)
@ -767,16 +807,35 @@ class bank(design.design):
def route_write_driver(self): def route_write_driver(self):
""" Connecting write driver """ """ Connecting write driver """
for port in range(self.total_ports): for port in self.all_ports:
for row in range(self.word_size): for row in range(self.word_size):
data_name = "data_{}".format(row) data_name = "data_{}".format(row)
din_name = "din{0}_{1}".format(port,row) din_name = "din{0}_{1}".format(port,row)
self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name) self.copy_layout_pin(self.write_driver_array_inst[port], data_name, din_name)
def connect_bitlines(self, top_inst, bottom_inst, num_items,
top_bl_name="bl_{}", top_br_name="br_{}", bottom_bl_name="bl_{}", bottom_br_name="br_{}"):
"""
Connect the bl and br of two modules.
This assumes that they have sufficient space to create a jog
in the middle between the two modules (if needed)
"""
for col in range(num_items):
bottom_bl = bottom_inst.get_pin(bottom_bl_name.format(col)).uc()
bottom_br = bottom_inst.get_pin(bottom_br_name.format(col)).uc()
top_bl = top_inst.get_pin(top_bl_name.format(col)).bc()
top_br = top_inst.get_pin(top_br_name.format(col)).bc()
yoffset = 0.5*(top_bl.y+bottom_bl.y)
self.add_path("metal2",[bottom_bl, vector(bottom_bl.x,yoffset),
vector(top_bl.x,yoffset), top_bl])
self.add_path("metal2",[bottom_br, vector(bottom_br.x,yoffset),
vector(top_br.x,yoffset), top_br])
def route_wordline_driver(self): def route_wordline_driver(self):
""" Connecting Wordline driver output to Bitcell WL connection """ """ Connecting Wordline driver output to Bitcell WL connection """
for port in range(self.total_ports): for port in self.all_ports:
for row in range(self.num_rows): for row in range(self.num_rows):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).rc() decoder_out_pos = self.row_decoder_inst[port].get_pin("decode_{}".format(row)).rc()
@ -787,7 +846,7 @@ class bank(design.design):
# The mid guarantees we exit the input cell to the right. # The mid guarantees we exit the input cell to the right.
driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).rc() driver_wl_pos = self.wordline_driver_inst[port].get_pin("wl_{}".format(row)).rc()
bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.total_wl_list[port]+"_{}".format(row)).lc() bitcell_wl_pos = self.bitcell_array_inst.get_pin(self.wl_names[port]+"_{}".format(row)).lc()
mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0) mid1 = driver_wl_pos.scale(0.5,1)+bitcell_wl_pos.scale(0.5,0)
mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1) mid2 = driver_wl_pos.scale(0.5,0)+bitcell_wl_pos.scale(0.5,1)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos]) self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
@ -798,7 +857,7 @@ class bank(design.design):
if not self.col_addr_size>0: if not self.col_addr_size>0:
return return
for port in range(self.total_ports): for port in self.all_ports:
if self.col_addr_size == 1: if self.col_addr_size == 1:
# Connect to sel[0] and sel[1] # Connect to sel[0] and sel[1]
@ -817,26 +876,16 @@ class bank(design.design):
addr_name = "addr{0}_{1}".format(port,i) addr_name = "addr{0}_{1}".format(port,i)
self.copy_layout_pin(self.col_decoder_inst[port], decoder_name, addr_name) self.copy_layout_pin(self.col_decoder_inst[port], decoder_name, addr_name)
offset = self.col_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
# This will do a quick "river route" on two layers. sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
# When above the top select line it will offset "inward" again to prevent conflicts.
# This could be done on a single layer, but we follow preferred direction rules for later routing.
top_y_offset = self.col_mux_array_inst[port].get_pin("sel_{}".format(self.num_col_addr_lines-1)).cy()
for (decode_name,i) in zip(decode_names,range(self.num_col_addr_lines)):
mux_name = "sel_{}".format(i)
mux_addr_pos = self.col_mux_array_inst[port].get_pin(mux_name).lc()
decode_out_pos = self.col_decoder_inst[port].get_pin(decode_name).center() route_map = list(zip(decode_names, sel_names))
decode_pins = {key: self.col_decoder_inst[port].get_pin(key) for key in decode_names }
# To get to the edge of the decoder and one track out col_mux_pins = {key: self.col_mux_array_inst[port].get_pin(key) for key in sel_names }
delta_offset = self.col_decoder_inst[port].rx() - decode_out_pos.x + self.m2_pitch # Combine the dff and bank pins into a single dictionary of pin name to pin.
if decode_out_pos.y > top_y_offset: all_pins = {**decode_pins, **col_mux_pins}
mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y) self.create_vertical_channel_route(route_map, all_pins, offset)
else:
mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y)
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
#self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
self.add_path("metal1",[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
@ -894,16 +943,16 @@ class bank(design.design):
read_inst = 0 read_inst = 0
# Control lines for RW ports # Control lines for RW ports
for port in range(self.total_ports): for port in self.all_ports:
connection = [] connection = []
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): if port in self.read_ports:
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[read_inst].get_pin("en").lc())) connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc()))
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"):
connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[write_inst].get_pin("en").lc())) if port in self.write_ports:
write_inst += 1 connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc()))
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"):
connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[read_inst].get_pin("en").lc())) if port in self.read_ports:
read_inst += 1 connection.append((self.prefix+"s_en{}".format(port), self.sense_amp_array_inst[port].get_pin("en").lc()))
for (control_signal, pin_pos) in connection: for (control_signal, pin_pos) in connection:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y) control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
@ -937,7 +986,7 @@ class bank(design.design):
bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew) bitcell_array_delay = self.bitcell_array.analytical_delay(word_driver_delay.slew)
#This also essentially creates the same delay for each port. Good structure, no substance #This also essentially creates the same delay for each port. Good structure, no substance
for port in range(self.total_ports): for port in self.all_ports:
if self.words_per_row > 1: if self.words_per_row > 1:
column_mux_delay = self.column_mux_array[port].analytical_delay(vdd, bitcell_array_delay.slew, column_mux_delay = self.column_mux_array[port].analytical_delay(vdd, bitcell_array_delay.slew,
self.sense_amp_array.input_load()) self.sense_amp_array.input_load())

View File

@ -125,10 +125,10 @@ class replica_bitline(design.design):
self.rbc_inst=self.add_inst(name="bitcell", self.rbc_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell) mod=self.replica_bitcell)
temp = [] temp = []
for port in range(self.total_ports): for port in self.all_ports:
temp.append("bl{}_0".format(port)) temp.append("bl{}_0".format(port))
temp.append("br{}_0".format(port)) temp.append("br{}_0".format(port))
for port in range(self.total_ports): for port in self.all_ports:
temp.append("delayed_en") temp.append("delayed_en")
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
@ -139,18 +139,18 @@ class replica_bitline(design.design):
mod=self.rbl) mod=self.rbl)
temp = [] temp = []
for port in range(self.total_ports): for port in self.all_ports:
temp.append("bl{}_0".format(port)) temp.append("bl{}_0".format(port))
temp.append("br{}_0".format(port)) temp.append("br{}_0".format(port))
for wl in range(self.bitcell_loads): for wl in range(self.bitcell_loads):
for port in range(self.total_ports): for port in self.all_ports:
temp.append("gnd") temp.append("gnd")
temp.append("vdd") temp.append("vdd")
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
self.wl_list = self.rbl.cell.list_all_wl_names() self.wl_list = self.rbl.cell.list_all_wl_names()
self.bl_list = self.rbl.cell.list_write_bl_names() self.bl_list = self.rbl.cell.list_all_bl_names()
def place_modules(self): def place_modules(self):
""" Add all of the module instances in the logical netlist """ """ Add all of the module instances in the logical netlist """
@ -195,7 +195,7 @@ class replica_bitline(design.design):
self.add_power_pin("gnd", pin_extension) self.add_power_pin("gnd", pin_extension)
# for multiport, need to short wordlines to each other so they all connect to gnd. # for multiport, need to short wordlines to each other so they all connect to gnd.
wl_last = self.wl_list[self.total_ports-1]+"_{}".format(row) wl_last = self.wl_list[-1]+"_{}".format(row)
pin_last = self.rbl_inst.get_pin(wl_last) pin_last = self.rbl_inst.get_pin(wl_last)
self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0)) self.short_wordlines(pin, pin_last, "right", False, row, vector(self.m3_pitch,0))
@ -203,7 +203,7 @@ class replica_bitline(design.design):
"""Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins.""" """Connects the word lines together for a single bitcell. Also requires which side of the bitcell to short the pins."""
#Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord. #Assumes input pins are wordlines. Also assumes the word lines are horizontal in metal1. Also assumes pins have same x coord.
#This is my (Hunter) first time editing layout in openram so this function is likely not optimal. #This is my (Hunter) first time editing layout in openram so this function is likely not optimal.
if self.total_ports > 1: if len(self.all_ports) > 1:
#1. Create vertical metal for all the bitlines to connect to #1. Create vertical metal for all the bitlines to connect to
#m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped #m1 needs to be extended in the y directions, direction needs to be determined as every other cell is flipped
correct_y = vector(0, 0.5*drc("minwidth_metal1")) correct_y = vector(0, 0.5*drc("minwidth_metal1"))
@ -234,7 +234,7 @@ class replica_bitline(design.design):
debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1) debug.error("Could not connect wordlines on specified input side={}".format(pin_side),1)
#2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this. #2. Connect word lines horizontally. Only replica cell needs. Bitline loads currently already do this.
for port in range(self.total_ports): for port in self.all_ports:
if is_replica_cell: if is_replica_cell:
wl = self.wl_list[port] wl = self.wl_list[port]
pin = self.rbc_inst.get_pin(wl) pin = self.rbc_inst.get_pin(wl)
@ -319,7 +319,7 @@ class replica_bitline(design.design):
# 4. Short wodlines if multiport # 4. Short wodlines if multiport
wl = self.wl_list[0] wl = self.wl_list[0]
wl_last = self.wl_list[self.total_ports-1] wl_last = self.wl_list[-1]
pin = self.rbc_inst.get_pin(wl) pin = self.rbc_inst.get_pin(wl)
pin_last = self.rbc_inst.get_pin(wl_last) pin_last = self.rbc_inst.get_pin(wl_last)
x_offset = self.short_wordlines(pin, pin_last, "left", True) x_offset = self.short_wordlines(pin, pin_last, "left", True)

View File

@ -26,6 +26,8 @@ class sense_amp(design.design):
def input_load(self): def input_load(self):
#Input load for the bitlines which are connected to the source/drain of a TX. Not the selects. #Input load for the bitlines which are connected to the source/drain of a TX. Not the selects.
from tech import spice, parameter from tech import spice, parameter
# Default is 8x. Per Samira and Hodges-Jackson book:
# "Column-mux transistors driven by the decoder must be sized for optimal speed"
bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file. bitline_pmos_size = 8 #FIXME: This should be set somewhere and referenced. Probably in tech file.
return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff return spice["min_tx_drain_c"]*(bitline_pmos_size/parameter["min_tx_size"])#ff

View File

@ -61,8 +61,7 @@ class single_level_column_mux_array(design.design):
def add_modules(self): def add_modules(self):
# FIXME: Why is this 8x? self.mux = single_level_column_mux(bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br)
self.mux = single_level_column_mux(tx_size=8, bitcell_bl=self.bitcell_bl, bitcell_br=self.bitcell_br)
self.add_mod(self.mux) self.add_mod(self.mux)

View File

@ -54,7 +54,9 @@ class precharge(pgate.pgate):
self.add_pin_list(["bl", "br", "en", "vdd"]) self.add_pin_list(["bl", "br", "en", "vdd"])
def add_ptx(self): def add_ptx(self):
"""Initializes the upper and lower pmos""" """
Initializes the upper and lower pmos
"""
self.pmos = ptx(width=self.ptx_width, self.pmos = ptx(width=self.ptx_width,
tx_type="pmos") tx_type="pmos")
self.add_mod(self.pmos) self.add_mod(self.pmos)
@ -63,33 +65,30 @@ class precharge(pgate.pgate):
def route_vdd_rail(self): def route_vdd_rail(self):
"""
Adds a vdd rail at the top of the cell
"""
"""Adds a vdd rail at the top of the cell""" # Adds the rail across the width of the cell
# adds the rail across the width of the cell vdd_position = vector(0.5*self.width, self.height)
vdd_position = vector(0, self.height - self.m1_width) self.add_rect_center(layer="metal1",
self.add_rect(layer="metal1",
offset=vdd_position, offset=vdd_position,
width=self.width, width=self.width,
height=self.m1_width) height=self.m1_width)
pmos_pin = self.upper_pmos2_inst.get_pin("S") pmos_pin = self.upper_pmos2_inst.get_pin("S")
# center of vdd rail # center of vdd rail
vdd_pos = vector(pmos_pin.cx(), vdd_position.y + 0.5*self.m1_width) pmos_vdd_pos = vector(pmos_pin.cx(), vdd_position.y)
self.add_path("metal1", [pmos_pin.uc(), vdd_pos]) self.add_path("metal1", [pmos_pin.uc(), pmos_vdd_pos])
# Add the M1->M2->M3 stack at the left edge # Add vdd pin above the transistor
vdd_contact_pos = vector(0.5*self.width, vdd_position.y + 0.5*self.m1_width) self.add_power_pin("vdd", pmos_pin.center(), rotate=0)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=vdd_contact_pos)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=vdd_contact_pos)
self.add_layout_pin_rect_center(text="vdd",
layer="metal3",
offset=vdd_contact_pos)
def create_ptx(self): def create_ptx(self):
"""Create both the upper_pmos and lower_pmos to the module""" """
Create both the upper_pmos and lower_pmos to the module
"""
self.lower_pmos_inst=self.add_inst(name="lower_pmos", self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos) mod=self.pmos)
@ -105,15 +104,19 @@ class precharge(pgate.pgate):
def place_ptx(self): def place_ptx(self):
"""Place both the upper_pmos and lower_pmos to the module""" """
Place both the upper_pmos and lower_pmos to the module
"""
# Compute the other pmos2 location, but determining offset to overlap the # Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins # source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# This is how much the contact is placed inside the ptx active
contact_xdiff = self.pmos.get_pin("S").lx()
# adds the lower pmos to layout # adds the lower pmos to layout
#base = vector(self.width - 2*self.pmos.width + self.overlap_offset.x, 0) bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
self.lower_pmos_position = vector(max(self.bitcell.get_pin(self.bitcell_bl).lx(), self.well_enclose_active), self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.well_enclose_active),
self.pmos.active_offset.y) self.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position) self.lower_pmos_inst.place(self.lower_pmos_position)
@ -122,11 +125,13 @@ class precharge(pgate.pgate):
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff) self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos) self.upper_pmos1_inst.place(self.upper_pmos1_pos)
upper_pmos2_pos = self.upper_pmos1_pos + self.overlap_offset upper_pmos2_pos = self.upper_pmos1_pos + overlap_offset
self.upper_pmos2_inst.place(upper_pmos2_pos) self.upper_pmos2_inst.place(upper_pmos2_pos)
def connect_poly(self): def connect_poly(self):
"""Connects the upper and lower pmos together""" """
Connects the upper and lower pmos together
"""
offset = self.lower_pmos_inst.get_pin("G").ll() offset = self.lower_pmos_inst.get_pin("G").ll()
# connects the top and bottom pmos' gates together # connects the top and bottom pmos' gates together
@ -145,7 +150,10 @@ class precharge(pgate.pgate):
height=self.poly_width) height=self.poly_width)
def route_en(self): def route_en(self):
"""Adds the en input rail, en contact/vias, and connects to the pmos""" """
Adds the en input rail, en contact/vias, and connects to the pmos
"""
# adds the en contact to connect the gates to the en rail on metal1 # adds the en contact to connect the gates to the en rail on metal1
offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space) offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space)
self.add_contact_center(layers=("poly", "contact", "metal1"), self.add_contact_center(layers=("poly", "contact", "metal1"),
@ -160,7 +168,10 @@ class precharge(pgate.pgate):
def place_nwell_and_contact(self): def place_nwell_and_contact(self):
"""Adds a nwell tap to connect to the vdd rail""" """
Adds a nwell tap to connect to the vdd rail
"""
# adds the contact from active to metal1 # adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active")) + vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active"))
@ -169,9 +180,10 @@ class precharge(pgate.pgate):
implant_type="n", implant_type="n",
well_type="n") well_type="n")
# leave an extra pitch for the height
self.height = well_contact_pos.y + contact.well.height + self.m1_pitch
self.height = well_contact_pos.y + contact.well.height # nwell should span the whole design since it is pmos only
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=vector(0,0), offset=vector(0,0),
width=self.width, width=self.width,
@ -179,10 +191,13 @@ class precharge(pgate.pgate):
def route_bitlines(self): def route_bitlines(self):
"""Adds both bit-line and bit-line-bar to the module""" """
Adds both bit-line and bit-line-bar to the module
"""
# adds the BL on metal 2 # adds the BL on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0) offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text="bl", self.bl_pin = self.add_layout_pin(text="bl",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
width=drc("minwidth_metal2"), width=drc("minwidth_metal2"),
@ -190,47 +205,61 @@ class precharge(pgate.pgate):
# adds the BR on metal 2 # adds the BR on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0) offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0)
self.add_layout_pin(text="br", self.br_pin = self.add_layout_pin(text="br",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
width=drc("minwidth_metal2"), width=drc("minwidth_metal2"),
height=self.height) height=self.height)
def connect_to_bitlines(self): def connect_to_bitlines(self):
"""
Connect the bitlines to the devices
"""
self.add_bitline_contacts() self.add_bitline_contacts()
self.connect_pmos(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br"))
def add_bitline_contacts(self): def add_bitline_contacts(self):
"""Adds contacts/via from metal1 to metal2 for bit-lines""" """
Adds contacts/via from metal1 to metal2 for bit-lines
"""
stack=("metal1", "via1", "metal2") stack=("metal1", "via1", "metal2")
pos = self.lower_pmos_inst.get_pin("S").center() upper_pin = self.upper_pmos1_inst.get_pin("S")
self.add_contact_center(layers=stack, lower_pin = self.lower_pmos_inst.get_pin("S")
offset=pos)
pos = self.lower_pmos_inst.get_pin("D").center()
self.add_contact_center(layers=stack,
offset=pos)
pos = self.upper_pmos1_inst.get_pin("S").center()
self.add_contact_center(layers=stack,
offset=pos)
pos = self.upper_pmos2_inst.get_pin("D").center()
self.add_contact_center(layers=stack,
offset=pos)
def connect_pmos(self, pmos_pin, bit_pin): # BL goes up to M2 at the transistor
""" Connect pmos pin to bitline pin """ self.bl_contact=self.add_contact_center(layers=stack,
offset=upper_pin.center())
self.add_contact_center(layers=stack,
offset=lower_pin.center())
ll_pos = vector(min(pmos_pin.lx(),bit_pin.lx()), pmos_pin.by()) # BR routes over on M1 first
ur_pos = vector(max(pmos_pin.rx(),bit_pin.rx()), pmos_pin.uy()) self.add_contact_center(layers=stack,
offset = vector(self.br_pin.cx(), upper_pin.cy()))
self.add_contact_center(layers=stack,
offset = vector(self.br_pin.cx(), lower_pin.cy()))
width = ur_pos.x-ll_pos.x def connect_pmos_m1(self, pmos_pin, bit_pin):
height = ur_pos.y-ll_pos.y """
self.add_rect(layer="metal2", Connect a pmos pin to bitline pin
offset=ll_pos, """
width=width,
height=height) left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
self.add_path("metal1", [ left_pos, right_pos] )
def connect_pmos_m2(self, pmos_pin, bit_pin):
"""
Connect a pmos pin to bitline pin
"""
left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
self.add_path("metal2", [ left_pos, right_pos], self.bl_contact.height)

View File

@ -9,18 +9,21 @@ from globals import OPTS
class single_level_column_mux(design.design): class single_level_column_mux(design.design):
""" """
This module implements the columnmux bitline cell used in the design. This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell. Creates a single columnmux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book:
Column-mux transistors driven by the decoder must be sized for optimal speed
""" """
# This is needed for different bitline spacings
unique_id = 1 unique_id = 1
def __init__(self, tx_size, bitcell_bl="bl", bitcell_br="br"): def __init__(self, tx_size=8, bitcell_bl="bl", bitcell_br="br"):
name="single_level_column_mux_{}_no{}".format(tx_size,single_level_column_mux.unique_id) self.tx_size = int(tx_size)
name="single_level_column_mux_{}_{}".format(self.tx_size,single_level_column_mux.unique_id)
single_level_column_mux.unique_id += 1 single_level_column_mux.unique_id += 1
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(2, "create single column mux cell: {0}".format(name)) debug.info(2, "create single column mux cell: {0}".format(name))
self.tx_size = tx_size
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br

View File

@ -566,11 +566,10 @@ class pin_group:
debug.info(2," Converting {0}".format(pin)) debug.info(2," Converting {0}".format(pin))
# Determine which tracks the pin overlaps # Determine which tracks the pin overlaps
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin) pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin)
pin_set.update(pin_in_tracks) pin_set.update(pin_in_tracks)
# Blockages will be a super-set of pins since it uses the inflated pin shape. # Blockages will be a super-set of pins since it uses the inflated pin shape.
blockage_in_tracks = self.router.convert_blockage(pin) blockage_in_tracks = self.router.convert_blockage(pin)
blockage_set.update(blockage_in_tracks) blockage_set.update(blockage_in_tracks)
# If we have a blockage, we must remove the grids # If we have a blockage, we must remove the grids
@ -578,17 +577,26 @@ class pin_group:
shared_set = pin_set & self.router.blocked_grids shared_set = pin_set & self.router.blocked_grids
if len(shared_set)>0: if len(shared_set)>0:
debug.info(2,"Removing pins {}".format(shared_set)) debug.info(2,"Removing pins {}".format(shared_set))
pin_set.difference_update(self.router.blocked_grids) pin_set.difference_update(shared_set)
shared_set = blockage_set & self.router.blocked_grids shared_set = blockage_set & self.router.blocked_grids
if len(shared_set)>0: if len(shared_set)>0:
debug.info(2,"Removing blocks {}".format(shared_set)) debug.info(2,"Removing blocks {}".format(shared_set))
blockage_set.difference_update(self.router.blocked_grids) blockage_set.difference_update(shared_set)
# At least one of the groups must have some valid tracks # At least one of the groups must have some valid tracks
if (len(pin_set)==0 and len(blockage_set)==0): if (len(pin_set)==0 and len(blockage_set)==0):
self.write_debug_gds("blocked_pin.gds") debug.warning("Pin is very close to metal blockage.\nAttempting to expand blocked pin {}".format(self.pins))
debug.error("Unable to find unblocked pin on grid.")
for pin_list in self.pins:
for pin in pin_list:
debug.info(2," Converting {0}".format(pin))
# Determine which tracks the pin overlaps
pin_in_tracks=self.router.convert_pin_to_tracks(self.name, pin, expansion=1)
pin_set.update(pin_in_tracks)
if len(pin_set)==0:
debug.error("Unable to find unblocked pin {} {}".format(self.name, self.pins))
self.router.write_debug_gds("blocked_pin.gds")
# We need to route each of the components, so don't combine the groups # We need to route each of the components, so don't combine the groups
self.grids = pin_set | blockage_set self.grids = pin_set | blockage_set

View File

@ -495,10 +495,11 @@ class router(router_tech):
# debug.info(0,"Pin {}".format(pin)) # debug.info(0,"Pin {}".format(pin))
return [ll,ur] return [ll,ur]
def convert_pin_to_tracks(self, pin_name, pin): def convert_pin_to_tracks(self, pin_name, pin, expansion=0):
""" """
Convert a rectangular pin shape into a list of track locations,layers. Convert a rectangular pin shape into a list of track locations,layers.
If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked. If no pins are "on-grid" (i.e. sufficient overlap) it makes the one with most overlap if it is not blocked.
If expansion>0, expamine areas beyond the current pin when it is blocked.
""" """
(ll,ur) = pin.rect (ll,ur) = pin.rect
debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur)) debug.info(3,"Converting pin [ {0} , {1} ]".format(ll,ur))
@ -512,8 +513,8 @@ class router(router_tech):
insufficient_list = set() insufficient_list = set()
zindex=self.get_zindex(pin.layer_num) zindex=self.get_zindex(pin.layer_num)
for x in range(int(ll[0]),int(ur[0])+1): for x in range(int(ll[0])+expansion,int(ur[0])+1+expansion):
for y in range(int(ll[1]),int(ur[1])+1): for y in range(int(ll[1]+expansion),int(ur[1])+1+expansion):
debug.info(4,"Converting [ {0} , {1} ]".format(x,y)) debug.info(4,"Converting [ {0} , {1} ]".format(x,y))
(full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex)) (full_overlap,partial_overlap) = self.convert_pin_coord_to_tracks(pin, vector3d(x,y,zindex))
if full_overlap: if full_overlap:
@ -523,9 +524,14 @@ class router(router_tech):
if len(sufficient_list)>0: if len(sufficient_list)>0:
return sufficient_list return sufficient_list
elif len(insufficient_list)>0: elif expansion==0 and len(insufficient_list)>0:
# If there wasn't a sufficient grid, find the best and patch it to be on grid. #Remove blockages and return the best to be patched
insufficient_list.difference_update(self.blocked_grids)
return self.get_best_offgrid_pin(pin, insufficient_list) return self.get_best_offgrid_pin(pin, insufficient_list)
elif expansion>0:
#Remove blockages and return the nearest
insufficient_list.difference_update(self.blocked_grids)
return self.get_nearest_offgrid_pin(pin, insufficient_list)
else: else:
debug.error("Unable to find any overlapping grids.", -1) debug.error("Unable to find any overlapping grids.", -1)
@ -555,6 +561,24 @@ class router(router_tech):
return set([best_coord]) return set([best_coord])
def get_nearest_offgrid_pin(self, pin, insufficient_list):
"""
Given a pin and a list of grid cells (probably non-overlapping),
return the nearest grid cell (center to center).
"""
#print("INSUFFICIENT LIST",insufficient_list)
# Find the coordinate with the most overlap
best_coord = None
best_dist = math.inf
for coord in insufficient_list:
track_pin = self.convert_track_to_pin(coord)
min_dist = pin.distance(track_pin)
if min_dist<best_dist:
best_dist=min_dist
best_coord=coord
return set([best_coord])
def convert_pin_coord_to_tracks(self, pin, coord): def convert_pin_coord_to_tracks(self, pin, coord):
""" """
@ -966,6 +990,7 @@ class router(router_tech):
""" """
Write out a GDS file with the routing grid and search information annotated on it. Write out a GDS file with the routing grid and search information annotated on it.
""" """
debug.info(0,"Writing annotated router gds file to {}".format(gds_name))
self.add_router_info() self.add_router_info()
self.cell.gds_write(gds_name) self.cell.gds_write(gds_name)

View File

@ -57,7 +57,7 @@ class sram_1bank(sram_base):
# the sense amps/column mux and cell array) # the sense amps/column mux and cell array)
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch) # The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# up to the row address DFFs. # up to the row address DFFs.
for port in range(self.total_ports): for port in self.all_ports:
control_pos = vector(-self.control_logic.width - 2*self.m2_pitch, control_pos = vector(-self.control_logic.width - 2*self.m2_pitch,
self.bank.bank_center.y - self.control_logic.control_logic_center.y) self.bank.bank_center.y - self.control_logic.control_logic_center.y)
self.control_logic_inst[port].place(control_pos) self.control_logic_inst[port].place(control_pos)
@ -94,11 +94,12 @@ class sram_1bank(sram_base):
""" """
Add the top-level pins for a single bank SRAM with control. Add the top-level pins for a single bank SRAM with control.
""" """
for port in range(self.total_ports): for port in self.all_ports:
# Connect the control pins as inputs # Connect the control pins as inputs
for signal in self.control_logic_inputs[port] + ["clk"]: for signal in self.control_logic_inputs[port] + ["clk"]:
self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port)) self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port))
if port in self.read_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "DOUT{0}[{1}]".format(port,bit)) self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "DOUT{0}[{1}]".format(port,bit))
@ -109,6 +110,7 @@ class sram_1bank(sram_base):
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
self.copy_layout_pin(self.row_addr_dff_inst[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size)) self.copy_layout_pin(self.row_addr_dff_inst[port], "din_{}".format(bit),"ADDR{0}[{1}]".format(port,bit+self.col_addr_size))
if port in self.write_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit)) self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit))
@ -132,7 +134,7 @@ class sram_1bank(sram_base):
""" Route the clock network """ """ Route the clock network """
# This is the actual input to the SRAM # This is the actual input to the SRAM
for port in range(self.total_ports): for port in self.all_ports:
self.copy_layout_pin(self.control_logic_inst[port], "clk", "clk{}".format(port)) self.copy_layout_pin(self.control_logic_inst[port], "clk", "clk{}".format(port))
# Connect all of these clock pins to the clock in the central bus # Connect all of these clock pins to the clock in the central bus
@ -172,7 +174,7 @@ class sram_1bank(sram_base):
def route_control_logic(self): def route_control_logic(self):
""" Route the outputs from the control logic module """ """ Route the outputs from the control logic module """
for port in range(self.total_ports): for port in self.all_ports:
for signal in self.control_logic_outputs[port]: for signal in self.control_logic_outputs[port]:
src_pin = self.control_logic_inst[port].get_pin(signal) src_pin = self.control_logic_inst[port].get_pin(signal)
dest_pin = self.bank_inst.get_pin(signal+"{}".format(port)) dest_pin = self.bank_inst.get_pin(signal+"{}".format(port))
@ -184,7 +186,7 @@ class sram_1bank(sram_base):
def route_row_addr_dff(self): def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """ """ Connect the output of the row flops to the bank pins """
for port in range(self.total_ports): for port in self.all_ports:
for bit in range(self.row_addr_size): for bit in range(self.row_addr_size):
flop_name = "dout_{}".format(bit) flop_name = "dout_{}".format(bit)
bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size) bank_name = "addr{0}_{1}".format(port,bit+self.col_addr_size)
@ -200,7 +202,7 @@ class sram_1bank(sram_base):
def route_col_addr_dff(self): def route_col_addr_dff(self):
""" Connect the output of the row flops to the bank pins """ """ Connect the output of the row flops to the bank pins """
for port in range(self.total_ports): for port in self.all_ports:
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)] bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1", col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch, pitch=self.m1_pitch,
@ -220,7 +222,7 @@ class sram_1bank(sram_base):
def route_data_dff(self): def route_data_dff(self):
""" 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 range(self.total_write): for port in self.write_ports:
offset = self.data_dff_inst[port].ul() + vector(0, self.m1_pitch) offset = self.data_dff_inst[port].ul() + vector(0, 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)]

View File

@ -24,38 +24,38 @@ class sram_base(design):
def add_pins(self): def add_pins(self):
""" Add pins for entire SRAM. """ """ Add pins for entire SRAM. """
for port in range(self.total_write): for port in self.write_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT") self.add_pin("DIN{0}[{1}]".format(port,bit),"INPUT")
for port in range(self.total_ports): for port in self.all_ports:
for bit in range(self.addr_size): for bit in range(self.addr_size):
self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT") self.add_pin("ADDR{0}[{1}]".format(port,bit),"INPUT")
# These are used to create the physical pins # These are used to create the physical pins
self.control_logic_inputs = [] self.control_logic_inputs = []
self.control_logic_outputs = [] self.control_logic_outputs = []
for port in range(self.total_ports): for port in self.all_ports:
if self.port_id[port] == "rw": if port in self.readwrite_ports:
self.control_logic_inputs.append(self.control_logic_rw.get_inputs()) self.control_logic_inputs.append(self.control_logic_rw.get_inputs())
self.control_logic_outputs.append(self.control_logic_rw.get_outputs()) self.control_logic_outputs.append(self.control_logic_rw.get_outputs())
elif self.port_id[port] == "w": elif port in self.write_ports:
self.control_logic_inputs.append(self.control_logic_w.get_inputs()) self.control_logic_inputs.append(self.control_logic_w.get_inputs())
self.control_logic_outputs.append(self.control_logic_w.get_outputs()) self.control_logic_outputs.append(self.control_logic_w.get_outputs())
else: else:
self.control_logic_inputs.append(self.control_logic_r.get_inputs()) self.control_logic_inputs.append(self.control_logic_r.get_inputs())
self.control_logic_outputs.append(self.control_logic_r.get_outputs()) self.control_logic_outputs.append(self.control_logic_r.get_outputs())
for port in range(self.total_ports): for port in self.all_ports:
self.add_pin("csb{}".format(port),"INPUT") self.add_pin("csb{}".format(port),"INPUT")
for port in range(self.num_rw_ports): for port in self.readwrite_ports:
self.add_pin("web{}".format(port),"INPUT") self.add_pin("web{}".format(port),"INPUT")
for port in range(self.total_ports): for port in self.all_ports:
self.add_pin("clk{}".format(port),"INPUT") self.add_pin("clk{}".format(port),"INPUT")
for port in range(self.total_read): for port in self.read_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
self.add_pin("DOUT{0}[{1}]".format(self.read_index[port],bit),"OUTPUT") self.add_pin("DOUT{0}[{1}]".format(port,bit),"OUTPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd","GROUND")
@ -138,7 +138,7 @@ class sram_base(design):
# Vertical bus # Vertical bus
# The order of the control signals on the control bus: # The order of the control signals on the control bus:
self.control_bus_names = [] self.control_bus_names = []
for port in range(self.total_ports): for port in self.all_ports:
self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)] self.control_bus_names[port] = ["clk_buf{}".format(port), "clk_buf_bar{}".format(port)]
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): if (self.port_id[port] == "rw") or (self.port_id[port] == "w"):
self.control_bus_names[port].append("w_en{}".format(port)) self.control_bus_names[port].append("w_en{}".format(port))
@ -268,23 +268,23 @@ class sram_base(design):
mod=self.bank)) mod=self.bank))
temp = [] temp = []
for port in range(self.total_read): for port in self.read_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
temp.append("DOUT{0}[{1}]".format(self.read_index[port],bit)) temp.append("DOUT{0}[{1}]".format(port,bit))
for port in range(self.total_write): for port in self.write_ports:
for bit in range(self.word_size): for bit in range(self.word_size):
temp.append("BANK_DIN{0}[{1}]".format(port,bit)) temp.append("BANK_DIN{0}[{1}]".format(port,bit))
for port in range(self.total_ports): for port in self.all_ports:
for bit in range(self.bank_addr_size): for bit in range(self.bank_addr_size):
temp.append("A{0}[{1}]".format(port,bit)) temp.append("A{0}[{1}]".format(port,bit))
if(self.num_banks > 1): if(self.num_banks > 1):
for port in range(self.total_ports): for port in self.all_ports:
temp.append("bank_sel{0}[{1}]".format(port,bank_num)) temp.append("bank_sel{0}[{1}]".format(port,bank_num))
for port in range(self.total_read): for port in self.read_ports:
temp.append("s_en{0}".format(self.read_index[port])) temp.append("s_en{0}".format(port))
for port in range(self.total_write): for port in self.write_ports:
temp.append("w_en{0}".format(port)) temp.append("w_en{0}".format(port))
for port in range(self.total_ports): for port in self.all_ports:
temp.append("clk_buf_bar{0}".format(port)) temp.append("clk_buf_bar{0}".format(port))
temp.append("clk_buf{0}".format(port)) temp.append("clk_buf{0}".format(port))
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
@ -327,7 +327,7 @@ class sram_base(design):
def create_row_addr_dff(self): def create_row_addr_dff(self):
""" Add all address flops for the main decoder """ """ Add all address flops for the main decoder """
insts = [] insts = []
for port in range(self.total_ports): for port in self.all_ports:
insts.append(self.add_inst(name="row_address{}".format(port), insts.append(self.add_inst(name="row_address{}".format(port),
mod=self.row_addr_dff)) mod=self.row_addr_dff))
@ -346,7 +346,7 @@ class sram_base(design):
def create_col_addr_dff(self): def create_col_addr_dff(self):
""" Add and place all address flops for the column decoder """ """ Add and place all address flops for the column decoder """
insts = [] insts = []
for port in range(self.total_ports): for port in self.all_ports:
insts.append(self.add_inst(name="col_address{}".format(port), insts.append(self.add_inst(name="col_address{}".format(port),
mod=self.col_addr_dff)) mod=self.col_addr_dff))
@ -365,7 +365,7 @@ class sram_base(design):
def create_data_dff(self): def create_data_dff(self):
""" Add and place all data flops """ """ Add and place all data flops """
insts = [] insts = []
for port in range(self.total_write): for port in self.write_ports:
insts.append(self.add_inst(name="data_dff{}".format(port), insts.append(self.add_inst(name="data_dff{}".format(port),
mod=self.data_dff)) mod=self.data_dff))
@ -384,10 +384,10 @@ class sram_base(design):
def create_control_logic(self): def create_control_logic(self):
""" Add and place control logic """ """ Add and place control logic """
insts = [] insts = []
for port in range(self.total_ports): for port in self.all_ports:
if self.port_id[port] == "rw": if port in self.readwrite_ports:
mod = self.control_logic_rw mod = self.control_logic_rw
elif self.port_id[port] == "w": elif port in self.write_ports:
mod = self.control_logic_w mod = self.control_logic_w
else: else:
mod = self.control_logic_r mod = self.control_logic_r
@ -396,12 +396,12 @@ class sram_base(design):
mod=mod)) mod=mod))
temp = ["csb{}".format(port)] temp = ["csb{}".format(port)]
if self.port_id[port] == "rw": if port in self.readwrite_ports:
temp.append("web{}".format(port)) temp.append("web{}".format(port))
temp.append("clk{}".format(port)) temp.append("clk{}".format(port))
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"): if port in self.read_ports:
temp.append("s_en{}".format(port)) temp.append("s_en{}".format(port))
if (self.port_id[port] == "rw") or (self.port_id[port] == "w"): if port in self.write_ports:
temp.append("w_en{}".format(port)) temp.append("w_en{}".format(port))
temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"]) temp.extend(["clk_buf_bar{}".format(port), "clk_buf{}".format(port), "vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)

View File

@ -33,6 +33,10 @@ class precharge_test(openram_test):
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl0", bitcell_br="br0") tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(tx) self.local_check(tx)
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl1", bitcell_br="br1")
self.local_check(tx)
debug.info(2, "Checking precharge for pbitcell (outermost connections)") debug.info(2, "Checking precharge for pbitcell (outermost connections)")
tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl2", bitcell_br="br2") tx = precharge.precharge(name="precharge_driver", size=1, bitcell_bl="bl2", bitcell_br="br2")
self.local_check(tx) self.local_check(tx)

View File

@ -24,18 +24,22 @@ class precharge_test(openram_test):
self.local_check(pc) self.local_check(pc)
# check precharge array in multi-port # check precharge array in multi-port
OPTS.bitcell = "pbitcell" OPTS.bitcell = "bitcell_1rw_1r"
OPTS.num_rw_ports = 1 OPTS.num_rw_ports = 1
OPTS.num_r_ports = 1 OPTS.num_r_ports = 1
OPTS.num_w_ports = 1 OPTS.num_w_ports = 0
debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)") debug.info(2, "Checking 3 column precharge array for 1RW/1R bitcell")
pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0") pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(pc) self.local_check(pc)
debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)") # debug.info(2, "Checking 3 column precharge array for pbitcell (innermost connections)")
pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl2", bitcell_br="br2") # pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl0", bitcell_br="br0")
self.local_check(pc) # self.local_check(pc)
# debug.info(2, "Checking 3 column precharge array for pbitcell (outermost connections)")
# pc = precharge_array.precharge_array(columns=3, bitcell_bl="bl2", bitcell_br="br2")
# self.local_check(pc)
globals.end_openram() globals.end_openram()

View File

@ -29,27 +29,33 @@ class psingle_bank_test(openram_test):
OPTS.num_r_ports = 0 OPTS.num_r_ports = 0
c = sram_config(word_size=4, c = sram_config(word_size=4,
num_words=16) num_words=16)
c.words_per_row=1 c.words_per_row=1
debug.info(1, "No column mux") debug.info(1, "No column mux")
a = bank(c, name="bank1_1rw_0w_0r_single") name = "bank1_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
self.local_check(a) self.local_check(a)
c.num_words=32 c.num_words=32
c.words_per_row=2 c.words_per_row=2
debug.info(1, "Two way column mux") debug.info(1, "Two way column mux")
a = bank(c, name="bank1_1rw_0w_0r_single") name = "bank2_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
self.local_check(a) self.local_check(a)
c.num_words=64 c.num_words=64
c.words_per_row=4 c.words_per_row=4
debug.info(1, "Four way column mux") debug.info(1, "Four way column mux")
a = bank(c, name="bank1_1rw_0w_0r_single") name = "bank3_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
self.local_check(a) self.local_check(a)
c.word_size=2
c.num_words=128 c.num_words=128
c.words_per_row=8 c.words_per_row=8
debug.info(1, "Four way column mux") debug.info(1, "Four way column mux")
a = bank(c, name="bank1_1rw_0w_0r_single") name = "bank4_{0}rw_{1}w_{2}r_single".format(OPTS.num_rw_ports, OPTS.num_w_ports, OPTS.num_r_ports)
a = bank(c, name=name)
self.local_check(a) self.local_check(a)

View File

@ -38,7 +38,6 @@ class single_bank_test(openram_test):
a = bank(c, name="bank3_single") a = bank(c, name="bank3_single")
self.local_check(a) self.local_check(a)
# Eight way has a short circuit of one column mux select to gnd rail
c.word_size=2 c.word_size=2
c.num_words=128 c.num_words=128
c.words_per_row=8 c.words_per_row=8

View File

@ -17,7 +17,7 @@ class timing_sram_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.spice_name="ngspice" OPTS.spice_name="ngspice"
OPTS.analytical_delay = False OPTS.analytical_delay = False
OPTS.trim_netlist = False OPTS.netlist_only = True
# This is a hack to reload the characterizer __init__ with the spice version # This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload from importlib import reload
@ -63,7 +63,7 @@ class timing_sram_test(openram_test):
elif OPTS.tech_name == "scn4m_subm": elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [3.644147], golden_data = {'delay_hl': [3.644147],
'delay_lh': [1.629815], 'delay_lh': [1.629815],
'leakage_power': 0.0009299118999999999, 'leakage_power': 0.001542964,
'min_period': 4.688, 'min_period': 4.688,
'read0_power': [16.28732], 'read0_power': [16.28732],
'read1_power': [15.75155], 'read1_power': [15.75155],

View File

@ -1,5 +1,6 @@
import unittest,warnings import unittest,warnings
import sys,os,glob,copy import sys,os,glob,copy
import shutil
sys.path.append(os.path.join(sys.path[0],"..")) sys.path.append(os.path.join(sys.path[0],".."))
from globals import OPTS from globals import OPTS
import debug import debug
@ -7,6 +8,7 @@ import debug
class openram_test(unittest.TestCase): class openram_test(unittest.TestCase):
""" Base unit test that we have some shared classes in. """ """ Base unit test that we have some shared classes in. """
def local_drc_check(self, w): def local_drc_check(self, w):
self.reset() self.reset()
@ -36,11 +38,17 @@ class openram_test(unittest.TestCase):
import verify import verify
result=verify.run_drc(a.name, tempgds) result=verify.run_drc(a.name, tempgds)
if result != 0: if result != 0:
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
#shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
self.fail("DRC failed: {}".format(a.name)) self.fail("DRC failed: {}".format(a.name))
result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) result=verify.run_lvs(a.name, tempgds, tempspice, final_verification)
if result != 0: if result != 0:
#zip_file = "/tmp/{0}_{1}".format(a.name,os.getpid())
#debug.info(0,"Archiving failed files to {}.zip".format(zip_file))
#shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
self.fail("LVS mismatch: {}".format(a.name)) self.fail("LVS mismatch: {}".format(a.name))
if OPTS.purge_temp: if OPTS.purge_temp:
@ -77,7 +85,8 @@ class openram_test(unittest.TestCase):
if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance): if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance):
data_matches = False data_matches = False
else: else:
self.isclose(k,data[k],golden_data[k],error_tolerance) if not self.isclose(k,data[k],golden_data[k],error_tolerance):
data_matches = False
if not data_matches: if not data_matches:
import pprint import pprint
data_string=pprint.pformat(data) data_string=pprint.pformat(data)