Convert port index lists to three simple lists.

This commit is contained in:
Matt Guthaus 2018-11-08 12:19:40 -08:00
parent b25650eb07
commit 7b10e3bfec
8 changed files with 350 additions and 300 deletions

View File

@ -50,31 +50,36 @@ class design(hierarchy_design):
self.implant_space = drc("implant_to_implant")
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
self.total_read = OPTS.num_rw_ports + OPTS.num_r_ports
self.total_ports = OPTS.num_rw_ports + OPTS.num_w_ports + OPTS.num_r_ports
self.num_rw_ports = OPTS.num_rw_ports
"""
These are contants and lists that aid multiport design.
Ports are always in the order RW, W, R.
Port indices start from 0 and increment.
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
for port in range(OPTS.num_rw_ports):
self.write_index.append(port_number)
self.read_index.append(port_number)
self.port_id.append("rw")
self.readwrite_ports.append(port_number)
self.write_ports.append(port_number)
self.read_ports.append(port_number)
port_number += 1
for port in range(OPTS.num_w_ports):
self.write_index.append(port_number)
self.port_id.append("w")
self.write_ports.append(port_number)
port_number += 1
for port in range(OPTS.num_r_ports):
self.read_index.append(port_number)
self.port_id.append("r")
self.read_ports.append(port_number)
port_number += 1
def analytical_power(self, proc, vdd, temp, load):

View File

@ -73,9 +73,9 @@ class delay(simulation):
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
if len(self.read_index) == 0:
if len(self.read_ports) == 0:
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)
def write_generic_stimulus(self):
@ -89,12 +89,12 @@ class delay(simulation):
self.sf.write("\n* Instantiation of the SRAM\n")
self.stim.inst_sram(sram=self.sram,
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,
dbits=self.word_size,
sram_name=self.name)
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):
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.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),
v1=0,
v2=self.vdd_voltage,
@ -171,24 +171,24 @@ class delay(simulation):
# generate data and addr signals
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):
self.stim.gen_constant(sig_name="{0}{1}_{2} ".format(self.din_name,write_port, i),
v_val=0)
for port in range(self.total_ports):
for port in self.all_ports:
for i in range(self.addr_size):
self.stim.gen_constant(sig_name="{0}{1}_{2}".format(self.addr_name,port, i),
v_val=0)
# generate control signals
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)
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.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.write_power_measures()
@ -317,7 +317,7 @@ class delay(simulation):
double the period until we find a valid period to use as a
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"])
time_out = 9
@ -362,18 +362,18 @@ class delay(simulation):
Loops through all read ports determining the feasible period and collecting
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
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
#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.
i = 1
while i < len(self.read_index):
port = self.read_index[i]
while i < len(self.read_ports):
port = self.read_ports[i]
#Only extract port values from the specified port, not the entire results.
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
@ -416,7 +416,7 @@ class delay(simulation):
#Sanity Check
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
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.
#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)
#The min period of one port becomes the new lower bound. Reset the upper_bound.
lb_period = target_period
@ -683,8 +683,8 @@ class delay(simulation):
"""Simulate all specified output loads and input slews pairs of all ports"""
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.
self.targ_read_ports = self.read_index
self.targ_write_ports = self.write_index
self.targ_read_ports = self.read_ports
self.targ_write_ports = self.write_ports
for slew in slews:
for load in loads:
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.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).
for port in range(self.total_ports):
for port in self.all_ports:
for mname,value in delay_results[port].items():
if "power" in mname:
# 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):
"""Returns the first accessible read or write port. """
if get_read_port and len(self.read_index) > 0:
return self.read_index[0]
elif not get_read_port and len(self.write_index) > 0:
return self.write_index[0]
if get_read_port and len(self.read_ports) > 0:
return self.read_ports[0]
elif not get_read_port and len(self.write_ports) > 0:
return self.write_ports[0]
return None
def 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):
"""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:
self.set_load_slew(load,slew)
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:
if "power" in mname:
port_data[port][mname].append(power.dynamic)
@ -877,7 +877,7 @@ class delay(simulation):
def gen_data(self):
""" 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):
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)
@ -887,16 +887,16 @@ class delay(simulation):
Generates the address inputs for a simulation timing test.
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):
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)
def gen_control(self):
""" 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)
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)
@ -904,5 +904,5 @@ class delay(simulation):
"""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
#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

View File

@ -80,8 +80,8 @@ class functional(simulation):
# Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously.
# This will test the viablilty of the transistor sizing in the bitcell.
for port in range(self.total_ports):
if self.port_id[port] == "w":
for port in self.all_ports:
if port in self.write_ports:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, port)
else:
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
for i in range(self.num_cycles):
w_addrs = []
for port in range(self.total_ports):
if self.port_id[port] == "rw":
for port in self.all_ports:
if port in self.readwrite_ports:
op = random.choice(rw_ops)
elif self.port_id[port] == "w":
elif port in self.write_ports:
op = random.choice(w_ops)
else:
op = random.choice(r_ops)
@ -225,17 +225,17 @@ class functional(simulation):
self.sf.write("\n* Instantiation of the SRAM\n")
self.stim.inst_sram(sram=self.sram,
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,
dbits=self.word_size,
sram_name=self.name)
# Add load capacitance to each of the read ports
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):
sig_name="{0}{1}_{2} ".format(self.dout_name, self.read_index[port], bit)
self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(self.read_index[port], bit, sig_name, self.load))
sig_name="{0}{1}_{2} ".format(self.dout_name, port, bit)
self.sf.write("CD{0}{1} {2} 0 {3}f\n".format(port, bit, sig_name, self.load))
# Write debug comments to stim file
self.sf.write("\n\n * Sequence of operations\n")
@ -244,27 +244,27 @@ class functional(simulation):
# Generate data input bits
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):
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)
# Generate address bits
for port in range(self.total_ports):
for port in self.all_ports:
for bit in range(self.addr_size):
sig_name="{0}{1}_{2} ".format(self.addr_name, port, bit)
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[port][bit], self.period, self.slew, 0.05)
# Generate control signals
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)
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)
# 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),
v1=self.gnd_voltage,
v2=self.vdd_voltage,

View File

@ -22,13 +22,10 @@ class simulation():
self.num_banks = self.sram.num_banks
self.sp_file = spfile
self.total_ports = self.sram.total_ports
self.total_write = self.sram.total_write
self.total_read = self.sram.total_read
self.read_index = self.sram.read_index
self.write_index = self.sram.write_index
self.num_rw_ports = self.sram.num_rw_ports
self.port_id = self.sram.port_id
self.all_ports = self.sram.all_ports
self.readwrite_ports = self.sram.readwrite_ports
self.read_ports = self.sram.read_ports
self.write_ports = self.sram.write_ports
def set_corner(self,corner):
""" Set the corner values """
@ -48,12 +45,12 @@ class simulation():
self.t_current = 0
# 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.web_values = [[] for port in range(self.num_rw_ports)]
self.csb_values = [[] for port in self.all_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
self.addr_values = [[[] for bit in range(self.addr_size)] for port in range(self.total_ports)]
self.data_values = [[[] for bit in range(self.word_size)] for port in range(self.total_write)]
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 self.write_ports]
# For generating comments in SPICE stimulus
self.cycle_comments = []
@ -75,7 +72,7 @@ class simulation():
# Append the values depending on the type of port
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 < self.num_rw_ports:
if port in self.readwrite_ports:
self.web_values[port].append(web_val)
def add_data(self, data, port):
@ -108,7 +105,7 @@ class simulation():
def add_write(self, comment, address, data, port):
""" 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)
self.fn_cycle_comments.append(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
noop_data = "0"*self.word_size
#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:
self.add_noop_one_port(address, noop_data, unselected_port)
def add_read(self, comment, address, din_data, port):
""" 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)
self.fn_cycle_comments.append(comment)
self.append_cycle_comment(port, comment)
@ -139,14 +136,14 @@ class simulation():
self.add_control_one_port(port, "read")
#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_address(address, 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
#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:
self.add_noop_one_port(address, noop_data, unselected_port)
@ -159,12 +156,12 @@ class simulation():
self.cycle_times.append(self.t_current)
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)
def add_write_one_port(self, comment, address, data, port):
""" 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)
self.fn_cycle_comments.append(comment)
@ -174,20 +171,20 @@ class simulation():
def add_read_one_port(self, comment, address, din_data, port):
""" 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)
self.fn_cycle_comments.append(comment)
self.add_control_one_port(port, "read")
#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_address(address, 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. """
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_address(address, port)

View File

@ -5,6 +5,7 @@ import design
import math
from math import log,sqrt,ceil
import contact
import pgates
from pinv import pinv
from pnand2 import pnand2
from pnor2 import pnor2
@ -66,26 +67,26 @@ class bank(design.design):
def add_pins(self):
""" 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):
self.add_pin("dout{0}_{1}".format(self.read_index[port],bit),"OUT")
for port in range(self.total_write):
self.add_pin("dout{0}_{1}".format(port,bit),"OUT")
for port in self.write_ports:
for bit in range(self.word_size):
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):
self.add_pin("addr{0}_{1}".format(port,bit),"INPUT")
# For more than one bank, we have a bank select and name
# the signals gated_*.
if self.num_banks > 1:
for port in range(self.total_ports):
for port in self.all_ports:
self.add_pin("bank_sel{}".format(port),"INPUT")
for port in range(self.total_read):
self.add_pin("s_en{0}".format(self.read_index[port]), "INPUT")
for port in range(self.total_write):
for port in self.read_ports:
self.add_pin("s_en{0}".format(port), "INPUT")
for port in self.write_ports:
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{0}".format(port),"INPUT")
self.add_pin("vdd","POWER")
@ -96,8 +97,8 @@ class bank(design.design):
""" Create routing amoung the modules """
self.route_central_bus()
self.route_precharge_to_bitcell_array()
self.route_col_mux_to_bitcell_array()
self.route_sense_amp_to_col_mux_or_bitcell_array()
self.route_col_mux_to_precharge_array()
self.route_sense_amp_to_col_mux_or_precharge_array()
self.route_sense_amp_out()
self.route_wordline_driver()
self.route_write_driver()
@ -127,26 +128,76 @@ class bank(design.design):
self.create_column_decoder()
self.create_bank_select()
def place_modules(self):
""" Add modules. The order should not matter! """
# Above the bitcell array
self.place_bitcell_array()
self.place_precharge_array()
def compute_module_offsets(self):
"""
Compute the module offsets.
"""
# UPPER RIGHT QUADRANT
# Bitcell array is placed at (0,0)
self.bitcell_array_offset = vector(0,0)
# LOWER RIGHT QUADRANT
# Below the bitcell array
self.place_column_mux_array()
self.place_sense_amp_array()
self.place_write_driver_array()
y_offset = self.precharge_array[0].height + self.m2_gap
self.precharge_offset = vector(0,-y_offset)
if self.col_addr_size > 0:
y_offset += self.column_mux_array[0].height
self.column_mux_offset = vector(0,-y_offset)
y_offset += self.sense_amp_array.height
self.sense_amp_offset = vector(0,-y_offset)
y_offset += self.write_driver_array.height
self.write_driver_offset = vector(0,-y_offset)
# UPPER LEFT QUADRANT
# To the left of the bitcell array
self.place_row_decoder()
self.place_wordline_driver()
self.place_column_decoder()
# The wordline driver is placed to the right of the main decoder width.
x_offset = self.central_bus_width + self.wordline_driver.width - self.m2_pitch
self.wordline_driver_offset = vector(-x_offset,0)
x_offset += self.row_decoder.width + self.m2_pitch
self.row_decoder_offset = vector(-x_offset,0)
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):
@ -184,7 +235,7 @@ class bank(design.design):
# These will be outputs of the gaters if this is multibank, if not, normal signals.
self.control_signals = []
for port in range(self.total_ports):
for port in self.all_ports:
if self.num_banks > 1:
self.control_signals.append(["gated_"+str for str in self.input_control_signals[port]])
else:
@ -226,30 +277,33 @@ class bank(design.design):
self.add_mod(self.bitcell_array)
# 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.read_br_list = self.bitcell.list_read_br_names()
self.read_bl_names = self.bitcell.list_read_bl_names()
self.read_br_names = self.bitcell.list_read_br_names()
self.write_bl_list = self.bitcell.list_write_bl_names()
self.write_br_list = self.bitcell.list_write_br_names()
self.write_bl_names = self.bitcell.list_write_bl_names()
self.write_br_names = self.bitcell.list_write_br_names()
self.total_bl_list = self.bitcell.list_all_bl_names()
self.total_br_list = self.bitcell.list_all_br_names()
self.total_bl_names = self.bitcell.list_all_bl_names()
self.total_br_names = 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.total_wl_names = self.bitcell.list_all_wl_names()
self.total_bitline_names = self.bitcell.list_all_bitline_names()
self.precharge_array = []
for port in range(self.total_read):
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]))
self.add_mod(self.precharge_array[port])
for port in self.all_ports:
if port in self.read_ports:
self.precharge_array.append(self.mod_precharge_array(columns=self.num_cols, bitcell_bl=self.total_bl_names[port], bitcell_br=self.total_br_names[port]))
self.add_mod(self.precharge_array[port])
else:
self.precharge_array.append(None)
if self.col_addr_size > 0:
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,
word_size=self.word_size,
bitcell_bl=self.total_bl_list[port],
bitcell_br=self.total_br_list[port]))
bitcell_bl=self.total_bl_names[port],
bitcell_br=self.total_br_names[port]))
self.add_mod(self.column_mux_array[port])
@ -284,151 +338,153 @@ class bank(design.design):
temp = []
for col in range(self.num_cols):
for bitline in self.total_bitline_list:
for bitline in self.total_bitline_names:
temp.append(bitline+"_{0}".format(col))
for row in range(self.num_rows):
for wordline in self.total_wl_list:
for wordline in self.total_wl_names:
temp.append(wordline+"_{0}".format(row))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_bitcell_array(self):
def place_bitcell_array(self, offset):
""" Placing Bitcell Array """
self.bitcell_array_inst.place(vector(0,0))
self.bitcell_array_inst.place(offset)
def create_precharge_array(self):
""" Creating Precharge """
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),
mod=self.precharge_array[port]))
temp = []
for i in range(self.num_cols):
temp.append(self.read_bl_list[port]+"_{0}".format(i))
temp.append(self.read_br_list[port]+"_{0}".format(i))
temp.extend([self.prefix+"clk_buf_bar{0}".format(self.read_index[port]), "vdd"])
temp.append(self.total_bl_names[port]+"_{0}".format(i))
temp.append(self.total_br_names[port]+"_{0}".format(i))
temp.extend([self.prefix+"clk_buf_bar{0}".format(port), "vdd"])
self.connect_inst(temp)
def place_precharge_array(self):
def place_precharge_array(self, offsets):
""" Placing Precharge """
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place precharge array.")
# FIXME: place for multiport
for port in range(self.total_read):
# The wells must be far enough apart
# 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))
for port in self.read_ports:
self.precharge_array_inst[port].place(offsets[port])
def create_column_mux_array(self):
""" Creating Column Mux when words_per_row > 1 . """
self.col_mux_array_inst = []
if self.col_addr_size == 0:
return
self.col_mux_array_inst = []
for port in range(self.total_ports):
for port in self.all_ports:
self.col_mux_array_inst.append(self.add_inst(name="column_mux_array{}".format(port),
mod=self.column_mux_array[port]))
temp = []
for col in range(self.num_cols):
temp.append(self.total_bl_list[port]+"_{0}".format(col))
temp.append(self.total_br_list[port]+"_{0}".format(col))
temp.append(self.total_bl_names[port]+"_{0}".format(col))
temp.append(self.total_br_names[port]+"_{0}".format(col))
for word in range(self.words_per_row):
temp.append("sel{0}_{1}".format(port,word))
for bit in range(self.word_size):
temp.append(self.total_bl_list[port]+"_out_{0}".format(bit))
temp.append(self.total_br_list[port]+"_out_{0}".format(bit))
temp.append(self.total_bl_names[port]+"_out_{0}".format(bit))
temp.append(self.total_br_names[port]+"_out_{0}".format(bit))
temp.append("gnd")
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 . """
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
if self.col_addr_size == 0:
return
for port in range(self.total_ports):
y_offset = self.column_mux_height
self.col_mux_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column mux array.")
for port in self.all_ports:
self.col_mux_array_inst[port].place(offsets[port])
def create_sense_amp_array(self):
""" Creating Sense amp """
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),
mod=self.sense_amp_array))
temp = []
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:
temp.append(self.read_bl_list[port]+"_{0}".format(bit))
temp.append(self.read_br_list[port]+"_{0}".format(bit))
temp.append(self.total_bl_names[port]+"_{0}".format(bit))
temp.append(self.total_br_names[port]+"_{0}".format(bit))
else:
temp.append(self.read_bl_list[port]+"_out_{0}".format(bit))
temp.append(self.read_br_list[port]+"_out_{0}".format(bit))
temp.append(self.total_bl_names[port]+"_out_{0}".format(bit))
temp.append(self.total_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)
def place_sense_amp_array(self):
def place_sense_amp_array(self, offsets):
""" Placing Sense amp """
debug.check(len(offsets)>=len(self.read_ports), "Insufficient offsets to place sense amp array.")
# FIXME: place for multiport
for port in range(self.total_read):
y_offset = self.column_mux_height + self.sense_amp_array.height + self.m2_gap
self.sense_amp_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
for port in self.read_ports:
self.sense_amp_array_inst[port].place(offsets[port])
def create_write_driver_array(self):
""" Creating Write Driver """
self.write_driver_array_inst = []
for port in range(self.total_write):
self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port),
mod=self.write_driver_array))
for port in self.all_ports:
if port in self.write_ports:
self.write_driver_array_inst.append(self.add_inst(name="write_driver_array{}".format(port),
mod=self.write_driver_array))
else:
self.write_driver_array_inst.append(None)
temp = []
for bit in range(self.word_size):
temp.append("din{0}_{1}".format(port,bit))
for bit in range(self.word_size):
if (self.words_per_row == 1):
temp.append(self.write_bl_list[port]+"_{0}".format(bit))
temp.append(self.write_br_list[port]+"_{0}".format(bit))
temp.append(self.total_bl_names[port]+"_{0}".format(bit))
temp.append(self.total_br_names[port]+"_{0}".format(bit))
else:
temp.append(self.write_bl_list[port]+"_out_{0}".format(bit))
temp.append(self.write_br_list[port]+"_out_{0}".format(bit))
temp.append(self.total_bl_names[port]+"_out_{0}".format(bit))
temp.append(self.total_br_names[port]+"_out_{0}".format(bit))
temp.extend([self.prefix+"w_en{0}".format(port), "vdd", "gnd"])
self.connect_inst(temp)
def place_write_driver_array(self):
def place_write_driver_array(self, offsets):
""" Placing Write Driver """
# FIXME: place for multiport
for port in range(self.total_write):
y_offset = self.sense_amp_array.height + self.column_mux_height \
+ self.m2_gap + self.write_driver_array.height
self.write_driver_array_inst[port].place(vector(0,y_offset).scale(-1,-1))
debug.check(len(offsets)>=len(self.write_ports), "Insufficient offsets to place write driver array.")
for port in self.write_ports:
self.write_driver_array_inst[port].place(offsets[port])
def create_row_decoder(self):
""" Create the hierarchical row decoder """
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),
mod=self.row_decoder))
@ -441,9 +497,11 @@ class bank(design.design):
self.connect_inst(temp)
def place_row_decoder(self):
def place_row_decoder(self, offsets):
""" 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
# 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.
@ -451,16 +509,15 @@ class bank(design.design):
# The address flop and decoder are aligned in the x coord.
# FIXME: place for multiport
for port in range(self.total_ports):
x_offset = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
self.row_decoder_inst[port].place(vector(x_offset,0))
for port in self.all_ports:
self.row_decoder_inst[port].place(offsets[port])
def create_wordline_driver(self):
""" Create the Wordline Driver """
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),
mod=self.wordline_driver))
@ -468,21 +525,20 @@ class bank(design.design):
for row in range(self.num_rows):
temp.append("dec_out{0}_{1}".format(port,row))
for row in range(self.num_rows):
temp.append(self.total_wl_list[port]+"_{0}".format(row))
temp.append(self.total_wl_names[port]+"_{0}".format(row))
temp.append(self.prefix+"clk_buf{0}".format(port))
temp.append("vdd")
temp.append("gnd")
self.connect_inst(temp)
def place_wordline_driver(self):
def place_wordline_driver(self, offsets):
""" Place the Wordline Driver """
# FIXME: place for multiport
for port in range(self.total_ports):
# The wordline driver is placed to the right of the main decoder width.
x_offset = -(self.central_bus_width + self.wordline_driver.width) + self.m2_pitch
self.wordline_driver_inst[port].place(vector(x_offset,0))
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place wordline driver array.")
for port in self.all_ports:
self.wordline_driver_inst[port].place(offsets[port])
def create_column_decoder(self):
@ -504,7 +560,7 @@ class bank(design.design):
debug.error("Invalid column decoder?",-1)
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),
mod=self.col_decoder))
@ -517,21 +573,17 @@ class bank(design.design):
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.
"""
if self.col_addr_size == 0:
return
# FIXME: place for multiport
for port in range(self.total_ports):
col_decoder_inst = self.col_decoder_inst[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))
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place column decoder.")
for port in self.all_ports:
col_decoder_inst.place(offsets[port])
@ -542,7 +594,7 @@ class bank(design.design):
return
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),
mod=self.bank_select))
@ -554,22 +606,16 @@ class bank(design.design):
self.connect_inst(temp)
def place_bank_select(self):
def place_bank_select(self, offsets):
""" Place the bank select logic. """
if not self.num_banks > 1:
return
# FIXME: place for multiport
for port in range(self.total_ports):
x_off = -(self.row_decoder.width + self.central_bus_width + self.wordline_driver.width)
if self.col_addr_size > 0:
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)
debug.check(len(offsets)>=len(self.all_ports), "Insufficient offsets to place bank select logic.")
for port in self.all_ports:
self.bank_select_inst[port].place(offsets[port])
def route_supplies(self):
@ -581,7 +627,7 @@ class bank(design.design):
def route_bank_select(self):
""" Route the bank select logic. """
for port in range(self.total_ports):
for port in self.all_ports:
if self.port_id[port] == "rw":
bank_sel_signals = ["clk_buf", "clk_buf_bar", "w_en", "s_en", "bank_sel"]
gated_bank_sel_signals = ["gated_clk_buf", "gated_clk_buf_bar", "gated_w_en", "gated_s_en"]
@ -641,7 +687,7 @@ class bank(design.design):
# The max point is always the top of the precharge bitlines
# Add a vdd and gnd power rail above the array
# 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.min_x_offset = self.row_decoder_inst[0].lx()
@ -661,7 +707,7 @@ class bank(design.design):
# and control lines.
# 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
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_length = self.max_y_offset - self.min_y_offset
self.bus_xoffset = self.create_bus(layer="metal2",
@ -677,12 +723,12 @@ class bank(design.design):
""" Routing of BL and BR between pre-charge and bitcell array """
# FIXME: Update for multiport
for port in range(self.total_read):
for port in self.read_ports:
for col in range(self.num_cols):
precharge_bl = self.precharge_array_inst[port].get_pin("bl_{}".format(col)).bc()
precharge_br = self.precharge_array_inst[port].get_pin("br_{}".format(col)).bc()
bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"_{}".format(col)).uc()
bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"_{}".format(col)).uc()
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)).uc()
bitcell_bl = self.bitcell_array_inst.get_pin(self.read_bl_names[port]+"_{}".format(col)).bc()
bitcell_br = self.bitcell_array_inst.get_pin(self.read_br_names[port]+"_{}".format(col)).bc()
yoffset = 0.5*(precharge_bl.y+bitcell_bl.y)
self.add_path("metal2",[precharge_bl, vector(precharge_bl.x,yoffset),
@ -691,32 +737,32 @@ class bank(design.design):
vector(bitcell_br.x,yoffset), bitcell_br])
def route_col_mux_to_bitcell_array(self):
""" Routing of BL and BR between col mux and bitcell array """
def route_col_mux_to_precharge_array(self):
""" Routing of BL and BR between col mux and precharge array """
# Only do this if we have a column mux!
if self.col_addr_size==0:
return
# FIXME: Update for multiport
for port in range(self.total_ports):
for port in self.all_ports:
for col in range(self.num_cols):
col_mux_bl = self.col_mux_array_inst[port].get_pin("bl_{}".format(col)).uc()
col_mux_br = self.col_mux_array_inst[port].get_pin("br_{}".format(col)).uc()
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()
precharge_bl = self.precharge_array_inst[port].get_pin(self.total_bl_names[port]+"_{}".format(col)).bc()
precharge_br = self.precharge_array_inst[port].get_pin(self.total_br_names[port]+"_{}".format(col)).bc()
yoffset = 0.5*(col_mux_bl.y+bitcell_bl.y)
yoffset = 0.5*(col_mux_bl.y+precharge_bl.y)
self.add_path("metal2",[col_mux_bl, vector(col_mux_bl.x,yoffset),
vector(bitcell_bl.x,yoffset), bitcell_bl])
vector(precharge_bl.x,yoffset), precharge_bl])
self.add_path("metal2",[col_mux_br, vector(col_mux_br.x,yoffset),
vector(bitcell_br.x,yoffset), bitcell_br])
vector(precharge_br.x,yoffset), precharge_br])
def route_sense_amp_to_col_mux_or_bitcell_array(self):
""" Routing of BL and BR between sense_amp and column mux or bitcell array """
def route_sense_amp_to_col_mux_or_precharge_array(self):
""" 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):
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()
@ -726,9 +772,9 @@ class bank(design.design):
connect_bl = self.col_mux_array_inst[port].get_pin("bl_out_{}".format(bit)).bc()
connect_br = self.col_mux_array_inst[port].get_pin("br_out_{}".format(bit)).bc()
else:
# Sense amp is directly connected to the bitcell array
connect_bl = self.bitcell_array_inst.get_pin(self.read_bl_list[port]+"_{}".format(bit)).bc()
connect_br = self.bitcell_array_inst.get_pin(self.read_br_list[port]+"_{}".format(bit)).bc()
# Sense amp is directly connected to the precharge array
connect_bl = self.precharge_array_inst[port].get_pin(self.read_bl_names[port]+"_{}".format(bit)).bc()
connect_br = self.precharge_array_inst[port].get_pin(self.read_br_names[port]+"_{}".format(bit)).bc()
yoffset = 0.5*(sense_amp_bl.y+connect_bl.y)
@ -742,10 +788,10 @@ class bank(design.design):
""" Add pins for the sense amp output """
# FIXME: Update for multiport
for port in range(self.total_read):
for port in self.read_ports:
for bit in range(self.word_size):
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,
offset=data_pin.center(),
height=data_pin.height(),
@ -757,7 +803,7 @@ class bank(design.design):
# FIXME: Update for multiport
# 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):
addr_idx = row + self.col_addr_size
decoder_name = "addr_{}".format(row)
@ -767,7 +813,7 @@ class bank(design.design):
def route_write_driver(self):
""" Connecting write driver """
for port in range(self.total_ports):
for port in self.all_ports:
for row in range(self.word_size):
data_name = "data_{}".format(row)
din_name = "din{0}_{1}".format(port,row)
@ -776,7 +822,7 @@ class bank(design.design):
def route_wordline_driver(self):
""" 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):
# 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()
@ -787,7 +833,7 @@ class bank(design.design):
# 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()
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.total_wl_names[port]+"_{}".format(row)).lc()
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)
self.add_path("metal1", [driver_wl_pos, mid1, mid2, bitcell_wl_pos])
@ -798,7 +844,7 @@ class bank(design.design):
if not self.col_addr_size>0:
return
for port in range(self.total_ports):
for port in self.all_ports:
if self.col_addr_size == 1:
# Connect to sel[0] and sel[1]
@ -894,16 +940,16 @@ class bank(design.design):
read_inst = 0
# Control lines for RW ports
for port in range(self.total_ports):
for port in self.all_ports:
connection = []
if (self.port_id[port] == "rw") or (self.port_id[port] == "r"):
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[read_inst].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()))
write_inst += 1
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()))
read_inst += 1
if port in self.read_ports:
connection.append((self.prefix+"clk_buf_bar{}".format(port), self.precharge_array_inst[port].get_pin("en").lc()))
if port in self.write_ports:
connection.append((self.prefix+"w_en{}".format(port), self.write_driver_array_inst[port].get_pin("en").lc()))
if port in self.read_ports:
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:
control_pos = vector(self.bus_xoffset[control_signal].x ,pin_pos.y)
@ -937,7 +983,7 @@ class bank(design.design):
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
for port in range(self.total_ports):
for port in self.all_ports:
if self.words_per_row > 1:
column_mux_delay = self.column_mux_array[port].analytical_delay(vdd, bitcell_array_delay.slew,
self.sense_amp_array.input_load())

View File

@ -125,10 +125,10 @@ class replica_bitline(design.design):
self.rbc_inst=self.add_inst(name="bitcell",
mod=self.replica_bitcell)
temp = []
for port in range(self.total_ports):
for port in self.all_ports:
temp.append("bl{}_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("vdd")
temp.append("gnd")
@ -139,11 +139,11 @@ class replica_bitline(design.design):
mod=self.rbl)
temp = []
for port in range(self.total_ports):
for port in self.all_ports:
temp.append("bl{}_0".format(port))
temp.append("br{}_0".format(port))
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("vdd")
temp.append("gnd")
@ -195,7 +195,7 @@ class replica_bitline(design.design):
self.add_power_pin("gnd", pin_extension)
# 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)
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."""
#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.
if self.total_ports > 1:
if len(self.all_ports) > 1:
#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
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)
#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:
wl = self.wl_list[port]
pin = self.rbc_inst.get_pin(wl)
@ -319,7 +319,7 @@ class replica_bitline(design.design):
# 4. Short wodlines if multiport
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_last = self.rbc_inst.get_pin(wl_last)
x_offset = self.short_wordlines(pin, pin_last, "left", True)

View File

@ -57,7 +57,7 @@ class sram_1bank(sram_base):
# the sense amps/column mux and cell array)
# The x-coordinate is placed to allow a single clock wire (plus an extra pitch)
# 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,
self.bank.bank_center.y - self.control_logic.control_logic_center.y)
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.
"""
for port in range(self.total_ports):
for port in self.all_ports:
# Connect the control pins as inputs
for signal in self.control_logic_inputs[port] + ["clk"]:
self.copy_layout_pin(self.control_logic_inst[port], signal, signal+"{}".format(port))
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))
if port in self.read_ports:
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
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):
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):
self.copy_layout_pin(self.data_dff_inst[port], "din_{}".format(bit), "DIN{0}[{1}]".format(port,bit))
if port in self.write_ports:
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):
""" Route a single bank SRAM """
@ -132,7 +134,7 @@ class sram_1bank(sram_base):
""" Route the clock network """
# 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))
# 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):
""" 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]:
src_pin = self.control_logic_inst[port].get_pin(signal)
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):
""" 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):
flop_name = "dout_{}".format(bit)
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):
""" 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)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch,
@ -220,7 +222,7 @@ class sram_1bank(sram_base):
def route_data_dff(self):
""" Connect the output of the data flops to the write driver """
# This is where the channel will start (y-dimension at least)
for port in range(self.total_write):
for port in self.write_ports:
offset = self.data_dff_inst[port].ul() + vector(0, self.m1_pitch)
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]

View File

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