mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' into datasheet_gen
This commit is contained in:
commit
62f8d26ec6
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,43 @@ class pin_layout:
|
||||||
return [dx,dy]
|
return [dx,dy]
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -905,4 +885,4 @@ class pbitcell(design.design):
|
||||||
leakage = spice["bitcell_leakage"]
|
leakage = spice["bitcell_leakage"]
|
||||||
dynamic = 0 #temporary
|
dynamic = 0 #temporary
|
||||||
total_power = self.return_power(dynamic, leakage)
|
total_power = self.return_power(dynamic, leakage)
|
||||||
return total_power
|
return total_power
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -127,26 +129,76 @@ class bank(design.design):
|
||||||
self.create_column_decoder()
|
self.create_column_decoder()
|
||||||
|
|
||||||
self.create_bank_select()
|
self.create_bank_select()
|
||||||
|
|
||||||
|
|
||||||
def place_modules(self):
|
def compute_module_offsets(self):
|
||||||
""" Add modules. The order should not matter! """
|
"""
|
||||||
|
Compute the module offsets.
|
||||||
# Above the bitcell array
|
"""
|
||||||
self.place_bitcell_array()
|
|
||||||
self.place_precharge_array()
|
|
||||||
|
|
||||||
|
# UPPER RIGHT QUADRANT
|
||||||
|
# Bitcell array is placed at (0,0)
|
||||||
|
self.bitcell_array_offset = vector(0,0)
|
||||||
|
|
||||||
|
# LOWER RIGHT QUADRANT
|
||||||
# Below the bitcell array
|
# Below the bitcell array
|
||||||
self.place_column_mux_array()
|
y_offset = self.precharge_array[0].height + self.m2_gap
|
||||||
self.place_sense_amp_array()
|
self.precharge_offset = vector(0,-y_offset)
|
||||||
self.place_write_driver_array()
|
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
|
# To the left of the bitcell array
|
||||||
self.place_row_decoder()
|
# The wordline driver is placed to the right of the main decoder width.
|
||||||
self.place_wordline_driver()
|
x_offset = self.central_bus_width + self.wordline_driver.width - self.m2_pitch
|
||||||
self.place_column_decoder()
|
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)
|
||||||
|
|
||||||
self.place_bank_select()
|
# 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):
|
||||||
|
""" Place the modules. """
|
||||||
|
|
||||||
|
self.compute_module_offsets()
|
||||||
|
|
||||||
|
# UPPER RIGHT QUADRANT
|
||||||
|
self.place_bitcell_array(self.bitcell_array_offset)
|
||||||
|
|
||||||
|
# LOWER RIGHT QUADRANT
|
||||||
|
self.place_precharge_array([self.precharge_offset]*len(self.read_ports))
|
||||||
|
self.place_column_mux_array([self.column_mux_offset]*len(self.all_ports))
|
||||||
|
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))
|
||||||
|
|
||||||
|
# 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.add_mod(self.precharge_array[port])
|
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])
|
||||||
|
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:
|
||||||
self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port),
|
if port in self.write_ports:
|
||||||
mod=self.write_driver_array))
|
self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port),
|
||||||
|
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]
|
for port in self.all_ports:
|
||||||
|
self.col_decoder_inst[port].place(offsets[port])
|
||||||
# Place the col decoder right aligned with row decoder
|
|
||||||
x_off = -(self.central_bus_width + self.wordline_driver.width + self.col_decoder.width)
|
|
||||||
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):
|
def route_sense_amp_to_col_mux_or_precharge_array(self):
|
||||||
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
|
""" Routing of BL and BR between sense_amp and column mux or precharge array """
|
||||||
|
|
||||||
for port in range(self.total_read):
|
for port in self.read_ports:
|
||||||
for bit in range(self.word_size):
|
bottom_inst = self.sense_amp_array_inst[port]
|
||||||
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()
|
if self.col_addr_size>0:
|
||||||
|
# Sense amp is connected to the col mux
|
||||||
if self.col_addr_size>0:
|
top_inst = self.col_mux_array_inst[port]
|
||||||
# Sense amp is connected to the col mux
|
top_bl = "bl_out_{}"
|
||||||
connect_bl = self.col_mux_array_inst[port].get_pin("bl_out_{}".format(bit)).bc()
|
top_br = "br_out_{}"
|
||||||
connect_br = self.col_mux_array_inst[port].get_pin("br_out_{}".format(bit)).bc()
|
else:
|
||||||
else:
|
# Sense amp is directly connected to the precharge array
|
||||||
# Sense amp is directly connected to the bitcell array
|
top_inst = self.precharge_array_inst[port]
|
||||||
connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"_{}".format(bit)).bc()
|
top_bl = "bl_{}"
|
||||||
connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"_{}".format(bit)).bc()
|
top_br = "br_{}"
|
||||||
|
|
||||||
|
self.connect_bitlines(top_inst, bottom_inst, self.word_size,
|
||||||
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
|
top_bl_name=top_bl, top_br_name=top_br)
|
||||||
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_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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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]
|
||||||
|
|
@ -816,27 +875,17 @@ class bank(design.design):
|
||||||
decoder_name = "in_{}".format(i)
|
decoder_name = "in_{}".format(i)
|
||||||
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)
|
||||||
|
|
||||||
|
|
||||||
# This will do a quick "river route" on two layers.
|
offset = self.col_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
|
||||||
# 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()
|
|
||||||
|
|
||||||
# To get to the edge of the decoder and one track out
|
sel_names = ["sel_{}".format(x) for x in range(self.num_col_addr_lines)]
|
||||||
delta_offset = self.col_decoder_inst[port].rx() - decode_out_pos.x + self.m2_pitch
|
|
||||||
if decode_out_pos.y > top_y_offset:
|
route_map = list(zip(decode_names, sel_names))
|
||||||
mid1_pos = vector(decode_out_pos.x + delta_offset + i*self.m2_pitch,decode_out_pos.y)
|
decode_pins = {key: self.col_decoder_inst[port].get_pin(key) for key in decode_names }
|
||||||
else:
|
col_mux_pins = {key: self.col_mux_array_inst[port].get_pin(key) for key in sel_names }
|
||||||
mid1_pos = vector(decode_out_pos.x + delta_offset + (self.num_col_addr_lines-i)*self.m2_pitch,decode_out_pos.y)
|
# Combine the dff and bank pins into a single dictionary of pin name to pin.
|
||||||
mid2_pos = vector(mid1_pos.x,mux_addr_pos.y)
|
all_pins = {**decode_pins, **col_mux_pins}
|
||||||
#self.add_wire(("metal1","via1","metal2"),[decode_out_pos, mid1_pos, mid2_pos, mux_addr_pos])
|
self.create_vertical_channel_route(route_map, all_pins, offset)
|
||||||
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())
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,58 +191,75 @@ 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"),
|
||||||
height=self.height)
|
height=self.height)
|
||||||
|
|
||||||
# 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()
|
# BL goes up to M2 at the transistor
|
||||||
self.add_contact_center(layers=stack,
|
self.bl_contact=self.add_contact_center(layers=stack,
|
||||||
offset=pos)
|
offset=upper_pin.center())
|
||||||
pos = self.upper_pmos1_inst.get_pin("S").center()
|
self.add_contact_center(layers=stack,
|
||||||
self.add_contact_center(layers=stack,
|
offset=lower_pin.center())
|
||||||
offset=pos)
|
|
||||||
pos = self.upper_pmos2_inst.get_pin("D").center()
|
# BR routes over on M1 first
|
||||||
self.add_contact_center(layers=stack,
|
self.add_contact_center(layers=stack,
|
||||||
offset=pos)
|
offset = vector(self.br_pin.cx(), upper_pin.cy()))
|
||||||
|
self.add_contact_center(layers=stack,
|
||||||
def connect_pmos(self, pmos_pin, bit_pin):
|
offset = vector(self.br_pin.cx(), lower_pin.cy()))
|
||||||
""" Connect pmos pin to bitline pin """
|
|
||||||
|
def connect_pmos_m1(self, pmos_pin, bit_pin):
|
||||||
ll_pos = vector(min(pmos_pin.lx(),bit_pin.lx()), pmos_pin.by())
|
"""
|
||||||
ur_pos = vector(max(pmos_pin.rx(),bit_pin.rx()), pmos_pin.uy())
|
Connect a pmos pin to bitline pin
|
||||||
|
"""
|
||||||
width = ur_pos.x-ll_pos.x
|
|
||||||
height = ur_pos.y-ll_pos.y
|
left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
|
||||||
self.add_rect(layer="metal2",
|
right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
|
||||||
offset=ll_pos,
|
|
||||||
width=width,
|
self.add_path("metal1", [ left_pos, right_pos] )
|
||||||
height=height)
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -52,7 +55,7 @@ class single_level_column_mux(design.design):
|
||||||
self.bitcell = self.mod_bitcell()
|
self.bitcell = self.mod_bitcell()
|
||||||
|
|
||||||
# Adds nmos_lower,nmos_upper to the module
|
# Adds nmos_lower,nmos_upper to the module
|
||||||
self.ptx_width = self.tx_size * drc("minwidth_tx")
|
self.ptx_width = self.tx_size*drc("minwidth_tx")
|
||||||
self.nmos = ptx(width=self.ptx_width)
|
self.nmos = ptx(width=self.ptx_width)
|
||||||
self.add_mod(self.nmos)
|
self.add_mod(self.nmos)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,13 +94,14 @@ 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))
|
||||||
|
|
||||||
for bit in range(self.word_size):
|
if port in self.read_ports:
|
||||||
self.copy_layout_pin(self.bank_inst, "dout{0}_{1}".format(port,bit), "DOUT{0}[{1}]".format(port,bit))
|
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))
|
||||||
|
|
||||||
# Lower address bits
|
# Lower address bits
|
||||||
for bit in range(self.col_addr_size):
|
for bit in range(self.col_addr_size):
|
||||||
|
|
@ -109,8 +110,9 @@ 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))
|
||||||
|
|
||||||
for bit in range(self.word_size):
|
if port in self.write_ports:
|
||||||
self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit))
|
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))
|
||||||
|
|
||||||
def route(self):
|
def route(self):
|
||||||
""" Route a single bank SRAM """
|
""" Route a single bank SRAM """
|
||||||
|
|
@ -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)]
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,10 @@ class precharge_test(openram_test):
|
||||||
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
|
debug.info(2, "Checking precharge for pbitcell (innermost connections)")
|
||||||
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")
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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],
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
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
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue