Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into multiport

This commit is contained in:
Michael Timothy Grimes 2018-08-05 19:53:28 -07:00
commit c2a9e91dba
30 changed files with 642 additions and 510 deletions

View File

@ -709,8 +709,8 @@ class layout(lef.lef):
def create_channel_route(self, route_map, top_pins, bottom_pins, offset,
layer_stack=("metal1", "via1", "metal2"), pitch=None,
vertical=False):
layer_stack=("metal1", "via1", "metal2"), pitch=None,
vertical=False):
"""
This is a simple channel route for one-to-one connections that
will jog the top route whenever there is a conflict. It does NOT
@ -749,13 +749,11 @@ class layout(lef.lef):
for top_name,top_pin in top_pins.items():
for bot_name,bot_pin in bottom_pins.items():
if not vertical and abs(top_pin.center().x-bot_pin.center().x) < pitch:
# The edges only go from top to bottom
# since we will order tracks bottom up
vcg[top_name].append(bot_name)
vcg[bot_name].append(top_name)
elif vertical and abs(top_pin.center().y-bot_pin.center().y) < pitch:
# The edges only go from top to bottom
# since we will order tracks bottom up
vcg[top_name].append(bot_name)
vcg[bot_name].append(top_name)
# This is the starting offset of the first trunk
if vertical:

View File

@ -89,7 +89,7 @@ class delay():
self.check_arguments()
# obtains list of time-points for each rising clk edge
self.obtain_cycle_times()
self.create_test_cycles()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
@ -105,25 +105,13 @@ class delay():
# generate data and addr signals
self.sf.write("\n* Generation of data and address signals\n")
for i in range(self.word_size):
if i == self.probe_data:
self.gen_data(clk_times=self.cycle_times,
sig_name="DIN[{0}]".format(i))
else:
self.stim.gen_constant(sig_name="DIN[{0}]".format(i),
v_val=0)
self.gen_addr(clk_times=self.cycle_times,
addr=self.probe_address)
self.gen_data()
self.gen_addr()
# generate control signals
self.sf.write("\n* Generation of control signals\n")
self.gen_csb(self.cycle_times)
self.gen_web(self.cycle_times)
self.gen_oeb(self.cycle_times)
self.gen_control()
self.sf.write("\n* Generation of global clock signal\n")
self.stim.gen_pulse(sig_name="CLK",
@ -149,13 +137,13 @@ class delay():
self.check_arguments()
# obtains list of time-points for each rising clk edge
self.obtain_cycle_times()
self.create_test_cycles()
# creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.sf = open(temp_stim, "w")
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period))
self.stim = stimuli.stimuli(self.sf, self.corner)
self.stim = stimuli(self.sf, self.corner)
# include UNTRIMMED files in stimulus file
if trim:
@ -213,20 +201,20 @@ class delay():
targ_name=targ_name,
trig_val=trig_val,
targ_val=targ_val,
trig_dir="FALL",
trig_dir="RISE",
targ_dir="FALL",
trig_td=self.cycle_times[self.read0_cycle],
targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period)
targ_td=self.cycle_times[self.read0_cycle])
self.stim.gen_meas_delay(meas_name="DELAY_LH",
trig_name=trig_name,
targ_name=targ_name,
trig_val=trig_val,
targ_val=targ_val,
trig_dir="FALL",
trig_dir="RISE",
targ_dir="RISE",
trig_td=self.cycle_times[self.read1_cycle],
targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period)
targ_td=self.cycle_times[self.read1_cycle])
self.stim.gen_meas_delay(meas_name="SLEW_HL",
trig_name=targ_name,
@ -236,7 +224,7 @@ class delay():
trig_dir="FALL",
targ_dir="FALL",
trig_td=self.cycle_times[self.read0_cycle],
targ_td=self.cycle_times[self.read0_cycle]+0.5*self.period)
targ_td=self.cycle_times[self.read0_cycle])
self.stim.gen_meas_delay(meas_name="SLEW_LH",
trig_name=targ_name,
@ -246,7 +234,7 @@ class delay():
trig_dir="RISE",
targ_dir="RISE",
trig_td=self.cycle_times[self.read1_cycle],
targ_td=self.cycle_times[self.read1_cycle]+0.5*self.period)
targ_td=self.cycle_times[self.read1_cycle])
# add measure statements for power
t_initial = self.cycle_times[self.write0_cycle]
@ -315,11 +303,11 @@ class delay():
feasible_delay_hl = results["delay_hl"]
feasible_slew_hl = results["slew_hl"]
debug.info(1, "Found feasible_period: {0}ns feasible_delay {1}ns/{2}ns slew {3}ns/{4}ns".format(feasible_period,
feasible_delay_lh,
feasible_delay_hl,
feasible_slew_lh,
feasible_slew_hl))
delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(feasible_delay_lh, feasible_delay_hl)
slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(feasible_slew_lh, feasible_slew_hl)
debug.info(1, "Found feasible_period: {0}ns {1} {2} ".format(feasible_period,
delay_str,
slew_str))
self.period = feasible_period
return (feasible_delay_lh, feasible_delay_hl)
@ -392,39 +380,32 @@ class delay():
""" Check if the measurements are defined and if they are valid. """
(delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple
period_load_slew_str = "period {0} load {1} slew {2}".format(self.period,self.load, self.slew)
# if it failed or the read was longer than a period
if type(delay_hl)!=float or type(delay_lh)!=float or type(slew_lh)!=float or type(slew_hl)!=float:
debug.info(2,"Failed simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
self.load,
self.slew,
delay_hl,
delay_lh,
slew_hl,
slew_lh))
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh)
debug.info(2,"Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
return False
# Scale delays to ns (they previously could have not been floats)
delay_hl *= 1e9
delay_lh *= 1e9
slew_hl *= 1e9
slew_lh *= 1e9
delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh)
if delay_hl>self.period or delay_lh>self.period or slew_hl>self.period or slew_lh>self.period:
debug.info(2,"UNsuccessful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
self.load,
self.slew,
delay_hl,
delay_lh,
slew_hl,
slew_lh))
debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
return False
else:
debug.info(2,"Successful simulation: period {0} load {1} slew {2}, delay_hl={3}n delay_lh={4}ns slew_hl={5}n slew_lh={6}n".format(self.period,
self.load,
self.slew,
delay_hl,
delay_lh,
slew_hl,
slew_lh))
debug.info(2,"Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delays_str,
slews_str))
return True
@ -547,6 +528,8 @@ class delay():
"""
Main function to characterize an SRAM for a table. Computes both delay and power characterization.
"""
# Data structure for all the characterization values
char_data = {}
self.set_probe(probe_address, probe_data)
@ -568,21 +551,27 @@ class delay():
debug.check(feasible_delay_lh>0,"Negative delay may not be possible")
debug.check(feasible_delay_hl>0,"Negative delay may not be possible")
# 2) Measure the delay, slew and power for all slew/load pairs.
# 2) Finds the minimum period without degrading the delays by X%
self.set_load_slew(max(loads),max(slews))
min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl)
debug.check(type(min_period)==float,"Couldn't find minimum period.")
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl))
char_data["min_period"] = round_time(min_period)
# Make a list for each type of measurement to append results to
char_data = {}
for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power",
"read1_power", "write0_power", "write1_power", "leakage_power"]:
char_data[m]=[]
# 2a) Find the leakage power of the trimmmed and UNtrimmed arrays.
# 3) Find the leakage power of the trimmmed and UNtrimmed arrays.
(full_array_leakage, trim_array_leakage)=self.run_power_simulation()
char_data["leakage_power"]=full_array_leakage
# 4) At the minimum period, measure the delay, slew and power for all slew/load pairs.
for slew in slews:
for load in loads:
self.set_load_slew(load,slew)
# 2c) Find the delay, dynamic power, and leakage power of the trimmed array.
# Find the delay, dynamic power, and leakage power of the trimmed array.
(success, delay_results) = self.run_delay_simulation()
debug.check(success,"Couldn't run a simulation. slew={0} load={1}\n".format(self.slew,self.load))
for k,v in delay_results.items():
@ -594,113 +583,161 @@ class delay():
# 3) Finds the minimum period without degrading the delays by X%
self.set_load_slew(max(loads),max(slews))
min_period = self.find_min_period(feasible_delay_lh, feasible_delay_hl)
debug.check(type(min_period)==float,"Couldn't find minimum period.")
debug.info(1, "Min Period: {0}n with a delay of {1} / {2}".format(min_period, feasible_delay_lh, feasible_delay_hl))
# 4) Pack up the final measurements
char_data["min_period"] = round_time(min_period)
return char_data
def obtain_cycle_times(self):
def add_data(self, data):
""" Add the array of data values """
debug.check(len(data)==self.word_size, "Invalid data word size.")
index = 0
for c in data:
if c=="0":
self.data_values[index].append(0)
elif c=="1":
self.data_values[index].append(1)
else:
debug.error("Non-binary data string",1)
index += 1
def add_address(self, address):
""" Add the array of address values """
debug.check(len(address)==self.addr_size, "Invalid address size.")
index = 0
for c in address:
if c=="0":
self.addr_values[index].append(0)
elif c=="1":
self.addr_values[index].append(1)
else:
debug.error("Non-binary address string",1)
index += 1
def add_noop(self, comment, address, data):
""" Add the control values for a read cycle. """
self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_times),
self.t_current,
comment))
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.web_values.append(1)
self.oeb_values.append(1)
self.csb_values.append(1)
self.add_data(data)
self.add_address(address)
def add_read(self, comment, address, data):
""" Add the control values for a read cycle. """
self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
self.t_current,
comment))
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.web_values.append(1)
self.oeb_values.append(0)
self.csb_values.append(0)
self.add_data(data)
self.add_address(address)
def add_write(self, comment, address, data):
""" Add the control values for a read cycle. """
self.cycle_comments.append("Cycle {0:2d}\t{1:5.2f}ns:\t{2}".format(len(self.cycle_comments),
self.t_current,
comment))
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.web_values.append(0)
self.oeb_values.append(1)
self.csb_values.append(0)
self.add_data(data)
self.add_address(address)
def create_test_cycles(self):
"""Returns a list of key time-points [ns] of the waveform (each rising edge)
of the cycles to do a timing evaluation. The last time is the end of the simulation
and does not need a rising edge."""
# Start at time 0
self.t_current = 0
# Cycle times (positive edge) with comment
self.cycle_comments = []
self.cycle_times = []
t_current = 0
# idle cycle, no operation
msg = "Idle cycle (no clock)"
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0,
t_current,
msg))
self.cycle_times.append(t_current)
t_current += self.period
# Control logic signals each cycle
self.web_values = []
self.oeb_values = []
self.csb_values = []
# One period
msg = "W data 1 address 11..11 to initialize cell"
self.cycle_times.append(t_current)
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += self.period
# Address and data values for each address/data bit
self.data_values=[]
for i in range(self.word_size):
self.data_values.append([])
self.addr_values=[]
for i in range(self.addr_size):
self.addr_values.append([])
# One period
msg = "W data 0 address 11..11 (to ensure a write of value works)"
self.cycle_times.append(t_current)
self.write0_cycle=len(self.cycle_times)-1
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += self.period
# Create the inverse address for a scratch address
inverse_address = ""
for c in self.probe_address:
if c=="0":
inverse_address += "1"
elif c=="1":
inverse_address += "0"
else:
debug.error("Non-binary address string",1)
# For now, ignore data patterns and write ones or zeros
data_ones = "1"*self.word_size
data_zeros = "0"*self.word_size
# One period
msg = "W data 1 address 00..00 (to clear bus caps)"
self.cycle_times.append(t_current)
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += self.period
self.add_noop("Idle cycle (no positive clock edge)",
inverse_address, data_zeros)
# One period
msg = "R data 0 address 11..11 to check W0 worked"
self.cycle_times.append(t_current)
self.read0_cycle=len(self.cycle_times)-1
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += self.period
self.add_write("W data 1 address 0..00",
inverse_address,data_ones)
# One period
msg = "Idle cycle (Read addr 00..00)"
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
self.cycle_times.append(t_current)
self.idle_cycle=len(self.cycle_times)-1
t_current += self.period
# One period
msg = "W data 1 address 11..11 (to ensure a write of value worked)"
self.cycle_times.append(t_current)
self.write1_cycle=len(self.cycle_times)-1
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += self.period
# One period
msg = "W data 0 address 00..00 (to clear bus caps)"
self.cycle_times.append(t_current)
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += self.period
self.add_write("W data 0 address 11..11 to write value",
self.probe_address,data_zeros)
self.write0_cycle=len(self.cycle_times)-1 # Remember for power measure
# One period
msg = "R data 1 address 11..11 to check W1 worked"
self.cycle_times.append(t_current)
self.read1_cycle=len(self.cycle_times)-1
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
t_current += self.period
# This also ensures we will have a H->L transition on the next read
self.add_read("R data 1 address 00..00 to set DOUT caps",
inverse_address,data_zeros)
# One period
msg = "Idle cycle (Read addr 11..11)"
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1,
t_current,
msg))
self.cycle_times.append(t_current)
t_current += self.period
self.add_read("R data 0 address 11..11 to check W0 worked",
self.probe_address,data_zeros)
self.read0_cycle=len(self.cycle_times)-1 # Remember for power measure
self.add_noop("Idle cycle (if read takes >1 cycle)",
inverse_address,data_zeros)
self.idle_cycle=len(self.cycle_times)-1 # Remember for power measure
self.add_write("W data 1 address 11..11 to write value",
self.probe_address,data_ones)
self.write1_cycle=len(self.cycle_times)-1 # Remember for power measure
self.add_write("W data 0 address 00..00 to clear DIN caps",
inverse_address,data_zeros)
# This also ensures we will have a L->H transition on the next read
self.add_read("R data 0 address 00..00 to clear DOUT caps",
inverse_address,data_zeros)
self.add_read("R data 1 address 11..11 to check W1 worked",
self.probe_address,data_zeros)
self.read1_cycle=len(self.cycle_times)-1 # Remember for power measure
self.add_noop("Idle cycle (if read takes >1 cycle))",
self.probe_address,data_zeros)
@ -741,48 +778,24 @@ class delay():
}
return data
def gen_data(self, clk_times, sig_name):
def gen_data(self):
""" Generates the PWL data inputs for a simulation timing test. """
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
# we are asserting the opposite value on the other side of the tx gate during
# the read to be "worst case". Otherwise, it can actually assist the read.
values = [0, 1, 0, 1, 1, 1, 1, 0, 0, 0 ]
self.stim.gen_pwl(sig_name, clk_times, values, self.period, self.slew, 0.05)
for i in range(self.word_size):
sig_name="DIN[{0}]".format(i)
self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[i], self.period, self.slew, 0.05)
def gen_addr(self, clk_times, addr):
def gen_addr(self):
"""
Generates the address inputs for a simulation timing test.
This alternates between all 1's and all 0's for the address.
"""
zero_values = [0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ]
ones_values = [1, 1, 1, 0, 1, 0, 1, 0, 1, 1 ]
for i in range(len(addr)):
for i in range(self.addr_size):
sig_name = "A[{0}]".format(i)
if addr[i]=="1":
self.stim.gen_pwl(sig_name, clk_times, ones_values, self.period, self.slew, 0.05)
else:
self.stim.gen_pwl(sig_name, clk_times, zero_values, self.period, self.slew, 0.05)
self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[i], self.period, self.slew, 0.05)
def gen_csb(self, clk_times):
""" Generates the PWL CSb signal """
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
# Keep CSb asserted in NOP for measuring >1 period
values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
self.stim.gen_pwl("csb", clk_times, values, self.period, self.slew, 0.05)
def gen_web(self, clk_times):
""" Generates the PWL WEb signal """
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP
# Keep WEb deasserted in NOP for measuring >1 period
values = [1, 0, 0, 0, 1, 1, 0, 0, 1, 1]
self.stim.gen_pwl("web", clk_times, values, self.period, self.slew, 0.05)
def gen_oeb(self, clk_times):
""" Generates the PWL WEb signal """
# values for NOP, W1, W0, W1, R0, W1, W0, R1, NOP
# Keep OEb asserted in NOP for measuring >1 period
values = [1, 1, 1, 1, 0, 0, 1, 1, 0, 0]
self.stim.gen_pwl("oeb", clk_times, values, self.period, self.slew, 0.05)
def gen_control(self):
""" Generates the control signals """
self.stim.gen_pwl("csb", self.cycle_times, self.csb_values, self.period, self.slew, 0.05)
self.stim.gen_pwl("web", self.cycle_times, self.web_values, self.period, self.slew, 0.05)
self.stim.gen_pwl("oeb", self.cycle_times, self.oeb_values, self.period, self.slew, 0.05)

View File

@ -300,28 +300,34 @@ class lib:
def write_data_bus(self):
""" Adds data bus timing results."""
self.lib.write(" bus(DATA){\n")
self.lib.write(" bus(DIN){\n")
self.lib.write(" bus_type : DATA; \n")
self.lib.write(" direction : inout; \n")
self.lib.write(" direction : in; \n")
# This is conservative, but limit to range that we characterized.
self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads)))
self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)))
self.lib.write(" three_state : \"!OEb & !clk\"; \n")
self.lib.write(" memory_write(){ \n")
self.lib.write(" address : ADDR; \n")
self.lib.write(" clocked_on : clk; \n")
self.lib.write(" }\n")
self.lib.write(" bus(DOUT){\n")
self.lib.write(" bus_type : DATA; \n")
self.lib.write(" direction : out; \n")
# This is conservative, but limit to range that we characterized.
self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads)))
self.lib.write(" min_capacitance : {0}; \n".format(min(self.loads)))
self.lib.write(" memory_read(){ \n")
self.lib.write(" address : ADDR; \n")
self.lib.write(" }\n")
self.lib.write(" pin(DATA[{0}:0]){{\n".format(self.sram.word_size - 1))
self.lib.write(" pin(DOUT[{0}:0]){{\n".format(self.sram.word_size - 1))
self.write_FF_setuphold()
self.lib.write(" timing(){ \n")
self.lib.write(" timing_sense : non_unate; \n")
self.lib.write(" related_pin : \"clk\"; \n")
self.lib.write(" timing_type : falling_edge; \n")
self.lib.write(" timing_type : rising_edge; \n")
self.lib.write(" cell_rise(CELL_TABLE) {\n")
self.write_values(self.char_results["delay_lh"],len(self.loads)," ")
self.lib.write(" }\n") # rise delay
@ -374,7 +380,7 @@ class lib:
self.lib.write(" pin(clk){\n")
self.lib.write(" clock : true;\n")
self.lib.write(" direction : input; \n")
# This should actually be a min inverter cap, but ok...
# FIXME: This depends on the clock buffer size in the control logic
self.lib.write(" capacitance : {0}; \n".format(tech.spice["dff_in_cap"]))
# Find the average power of 1 and 0 bits for writes and reads over all loads/slews

View File

@ -144,7 +144,7 @@ class stimuli():
times = np.array(clk_times) - setup*period
values = np.array(data_values) * self.voltage
half_slew = 0.5 * slew
self.sf.write("* (time, data): {}\n".format(zip(clk_times, data_values)))
self.sf.write("* (time, data): {}\n".format(list(zip(clk_times, data_values))))
self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
for i in range(1,len(times)):
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew,

View File

@ -55,7 +55,7 @@ class trim_spice():
col_address = 0
# 1. Keep cells in the bitcell array based on WL and BL
wl_name = "wl[{}]".format(wl_address)
bl_name = "bl[{}]".format(self.words_per_row*data_bit + col_address)
bl_name = "bl[{}]".format(int(self.words_per_row*data_bit + col_address))
# Prepend info about the trimming
addr_msg = "Keeping {} address".format(address)

View File

@ -61,7 +61,9 @@ class control_logic(design.design):
self.add_mod(self.nand3)
# Special gates: inverters for buffering
self.clkbuf = pinvbuf(4,16,height=dff_height)
# Size the clock for the number of rows (fanout)
clock_driver_size = max(1,int(self.num_rows/4))
self.clkbuf = pinvbuf(clock_driver_size,height=dff_height)
self.add_mod(self.clkbuf)
self.inv = self.inv1 = pinv(size=1, height=dff_height)
self.add_mod(self.inv1)
@ -345,7 +347,7 @@ class control_logic(design.design):
x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row)
# input: WE, clk_buf_bar, CS output: w_en_bar
# input: WE, CS output: w_en_bar
w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar",
mod=self.nand3,

View File

@ -20,6 +20,12 @@ class dff_buf(design.design):
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
# contact does not violate spacing to the rail in the NMOS.
debug.check(inv1_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
debug.check(inv2_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
from importlib import reload
c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff)

View File

@ -12,19 +12,24 @@ class dff_inv(design.design):
do not have Qbar, so this will create it.
"""
def __init__(self, inv_size=1, name=""):
def __init__(self, inv_size=2, name=""):
if name=="":
name = "dff_inv_{0}".format(inv_size)
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
# This is specifically for SCMOS where the DFF vdd/gnd rails are more than min width.
# This causes a DRC in the pinv which assumes min width rails. This ensures the output
# contact does not violate spacing to the rail in the NMOS.
debug.check(inv_size>=2, "Inverter must be greater than two for rail spacing DRC rules.")
from importlib import reload
c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff)
self.dff = self.mod_dff("dff")
self.add_mod(self.dff)
self.inv1 = pinv(size=inv_size,height=self.dff.height)
self.add_mod(self.inv1)

View File

@ -127,14 +127,14 @@ class wordline_driver(design.design):
mirror=inst_mirror))
self.connect_inst(["en_bar[{0}]".format(row),
"in[{0}]".format(row),
"net[{0}]".format(row),
"wl_bar[{0}]".format(row),
"vdd", "gnd"])
# add inv2
self.inv2_inst.append(self.add_inst(name=name_inv2,
mod=self.inv,
offset=inv2_offset,
mirror=inst_mirror))
self.connect_inst(["net[{0}]".format(row),
self.connect_inst(["wl_bar[{0}]".format(row),
"wl[{0}]".format(row),
"vdd", "gnd"])

View File

@ -96,7 +96,9 @@ class pinv(pgate.pgate):
nmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"]
pmos_height_available = 0.5 * tx_height_available - 0.5*drc["poly_to_poly"]
debug.info(2,"Height avail {0} PMOS height {1} NMOS height {2}".format(tx_height_available, nmos_height_available, pmos_height_available))
debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
nmos_height_available,
pmos_height_available))
# Determine the number of mults for each to fit width into available space
self.nmos_width = self.nmos_size*drc["minwidth_tx"]

View File

@ -15,20 +15,35 @@ class pinvbuf(design.design):
c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, OPTS.bitcell)
def __init__(self, inv1_size=2, inv2_size=4, height=bitcell.height, name=""):
unique_id = 1
def __init__(self, driver_size=4, height=bitcell.height, name=""):
stage_effort = 4
# FIXME: Change the number of stages to support high drives.
# stage effort of 4 or less
# The pinvbuf has a FO of 2 for the first stage, so the second stage
# should be sized "half" to prevent loading of the first stage
predriver_size = max(int(driver_size/(stage_effort/2)),1)
if name=="":
name = "pinvbuf_{0}_{1}".format(inv1_size, inv2_size)
name = "pinvbuf_{0}_{1}_{2}".format(predriver_size, driver_size, pinvbuf.unique_id)
pinvbuf.unique_id += 1
design.design.__init__(self, name)
debug.info(1, "Creating {}".format(self.name))
self.inv = pinv(size=1, height=height)
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(predriver_size/stage_effort))
self.inv = pinv(size=input_size, height=height)
self.add_mod(self.inv)
self.inv1 = pinv(size=inv1_size, height=height)
self.inv1 = pinv(size=predriver_size, height=height)
self.add_mod(self.inv1)
self.inv2 = pinv(size=inv2_size, height=height)
self.inv2 = pinv(size=driver_size, height=height)
self.add_mod(self.inv2)
self.width = 2*self.inv1.width + self.inv2.width

View File

@ -21,7 +21,7 @@ class pinvbuf_test(openram_test):
import pinvbuf
debug.info(2, "Testing inverter/buffer 4x 8x")
a = pinvbuf.pinvbuf(4,8)
a = pinvbuf.pinvbuf(8)
self.local_check(a)
globals.end_openram()

View File

@ -41,7 +41,7 @@ class timing_sram_test(openram_test):
probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner)
@ -49,41 +49,36 @@ class timing_sram_test(openram_test):
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data, slews, loads)
#print data
if OPTS.tech_name == "freepdk45":
golden_data = {'leakage_power': 0.0006964536000000001,
'delay_lh': [0.0573055],
'read0_power': [0.0337812],
'read1_power': [0.032946500000000004],
'write1_power': [0.0361529],
'write0_power': [0.026179099999999997],
'slew_hl': [0.0285185],
'min_period': 0.205,
'delay_hl': [0.070554],
'slew_lh': [0.0190073]}
golden_data = {'delay_hl': [2.5614],
'delay_lh': [0.22929839999999999],
'leakage_power': 0.0020326,
'min_period': 4.844,
'read0_power': [0.0497676],
'read1_power': [0.0463576],
'slew_hl': [0.1119293],
'slew_lh': [0.0237043],
'write0_power': [0.0494321],
'write1_power': [0.0457268]}
elif OPTS.tech_name == "scn3me_subm":
golden_data = {'leakage_power': 0.0004004581,
'delay_lh': [0.6538954],
'read0_power': [9.7622],
'read1_power': [9.589],
'write1_power': [10.8],
'write0_power': [6.928400000000001],
'slew_hl': [0.8321625],
'min_period': 2.344,
'delay_hl': [0.9019090999999999],
'slew_lh': [0.5896232]}
golden_data = {'delay_hl': [6.0052],
'delay_lh': [2.2886],
'leakage_power': 0.025629199999999998,
'min_period': 9.375,
'read0_power': [8.8721],
'read1_power': [8.3179],
'slew_hl': [1.0746],
'slew_lh': [0.413426],
'write0_power': [8.6601],
'write1_power': [8.0397]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys()))
# Check each result
for k in data.keys():
if type(data[k])==list:
for i in range(len(data[k])):
self.isclose(data[k][i],golden_data[k][i],0.15)
else:
self.isclose(data[k],golden_data[k],0.15)
self.assertTrue(self.check_golden_data(data,golden_data,0.25))
globals.end_openram()
# instantiate a copdsay of the class to actually run the test

View File

@ -49,13 +49,8 @@ class timing_setup_test(openram_test):
# Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys()))
# Check each result
for k in data.keys():
if type(data[k])==list:
for i in range(len(data[k])):
self.isclose(data[k][i],golden_data[k][i],0.15)
else:
self.isclose(data[k],golden_data[k],0.15)
self.assertTrue(self.check_golden_data(data,golden_data,0.25))
globals.end_openram()

View File

@ -17,6 +17,7 @@ class timing_sram_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.spice_name="ngspice"
OPTS.analytical_delay = False
OPTS.trim_netlist = False
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
@ -39,7 +40,7 @@ class timing_sram_test(openram_test):
probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
debug.info(1, "Probe address {0} probe data bit {1}".format(probe_address, probe_data))
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner)
@ -47,41 +48,36 @@ class timing_sram_test(openram_test):
loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data, slews, loads)
#print data
if OPTS.tech_name == "freepdk45":
golden_data = {'leakage_power': 0.0007348262,
'delay_lh': [0.05799613],
'read0_power': [0.0384102],
'read1_power': [0.03279848],
'write1_power': [0.03693655],
'write0_power': [0.02717752],
'slew_hl': [0.03607912],
'min_period': 0.742,
'delay_hl': [0.3929995],
'slew_lh': [0.02160862]}
golden_data = {'delay_hl': [2.562671],
'delay_lh': [0.2320771],
'leakage_power': 0.00102373,
'min_period': 4.844,
'read0_power': [0.047404110000000006],
'read1_power': [0.0438884],
'slew_hl': [0.1140206],
'slew_lh': [0.02492785],
'write0_power': [0.04765188],
'write1_power': [0.04434999]}
elif OPTS.tech_name == "scn3me_subm":
golden_data = {'leakage_power': 0.00142014,
'delay_lh': [0.8018421],
'read0_power': [11.44908],
'read1_power': [11.416549999999999],
'write1_power': [11.718020000000001],
'write0_power': [8.250219],
'slew_hl': [0.8273725],
'min_period': 34,
'delay_hl': [1.085861],
'slew_lh': [0.5730144]}
golden_data = {'delay_hl': [11.69536],
'delay_lh': [1.260921],
'leakage_power': 0.00039469710000000004,
'min_period': 20.0,
'read0_power': [4.40238],
'read1_power': [4.126633],
'slew_hl': [1.259555],
'slew_lh': [0.9150649],
'write0_power': [4.988347],
'write1_power': [4.473887]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys()))
# Check each result
for k in data.keys():
if type(data[k])==list:
for i in range(len(data[k])):
self.isclose(data[k][i],golden_data[k][i],0.15)
else:
self.isclose(data[k],golden_data[k],0.15)
self.assertTrue(self.check_golden_data(data,golden_data,0.25))
globals.end_openram()

View File

@ -49,14 +49,9 @@ class timing_setup_test(openram_test):
# Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys()))
# Check each result
for k in data.keys():
if type(data[k])==list:
for i in range(len(data[k])):
self.isclose(data[k][i],golden_data[k][i],0.15)
else:
self.isclose(data[k],golden_data[k],0.15)
self.assertTrue(self.check_golden_data(data,golden_data,0.25))
reload(characterizer)
globals.end_openram()

View File

@ -40,7 +40,7 @@ class lib_test(openram_test):
newname = filename.replace(".lib","_analytical.lib")
libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname)
self.isapproxdiff(libname,golden,0.15)
self.assertTrue(self.isapproxdiff(libname,golden,0.15))
globals.end_openram()

View File

@ -49,7 +49,7 @@ class lib_test(openram_test):
newname = filename.replace(".lib","_pruned.lib")
libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname)
self.isapproxdiff(libname,golden,0.40)
self.assertTrue(self.isapproxdiff(libname,golden,0.40))
reload(characterizer)
globals.end_openram()

View File

@ -48,7 +48,7 @@ class lib_test(openram_test):
for filename in lib_files:
libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
self.isapproxdiff(libname,golden,0.40)
self.assertTrue(self.isapproxdiff(libname,golden,0.40))
reload(characterizer)
globals.end_openram()

View File

@ -64,10 +64,13 @@ class openram_test(openram_test):
self.assertTrue(len(files)>0)
# grep any errors from the output
output = open("{0}/output.log".format(out_path),"r").read()
output_log = open("{0}/output.log".format(out_path),"r")
output = output_log.read()
output_log.close()
self.assertEqual(len(re.findall('ERROR',output)),0)
self.assertEqual(len(re.findall('WARNING',output)),0)
# now clean up the directory
if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True)

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 1032.3999375;
area : 948.52275;
leakage_power () {
when : "CSb";
value : 0.0008128352;
value : 0.0021292;
}
cell_leakage_power : 0;
bus(DATA){
bus(DIN){
bus_type : DATA;
direction : inout;
direction : in;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
memory_read(){
address : ADDR;
}
pin(DATA[1:0]){
pin(DOUT[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk";
@ -130,26 +134,26 @@ cell (sram_2_16_1_freepdk45){
timing(){
timing_sense : non_unate;
related_pin : "clk";
timing_type : falling_edge;
timing_type : rising_edge;
cell_rise(CELL_TABLE) {
values("0.055, 0.056, 0.063",\
"0.056, 0.057, 0.063",\
"0.061, 0.062, 0.069");
values("0.229, 0.23, 0.234",\
"0.23, 0.23, 0.234",\
"0.236, 0.236, 0.24");
}
cell_fall(CELL_TABLE) {
values("0.067, 0.068, 0.076",\
"0.067, 0.068, 0.077",\
"0.073, 0.074, 0.082");
values("2.555, 2.556, 2.568",\
"2.555, 2.557, 2.569",\
"2.562, 2.563, 2.575");
}
rise_transition(CELL_TABLE) {
values("0.013, 0.015, 0.026",\
"0.013, 0.015, 0.026",\
"0.014, 0.015, 0.026");
values("0.02, 0.021, 0.028",\
"0.02, 0.021, 0.028",\
"0.02, 0.021, 0.028");
}
fall_transition(CELL_TABLE) {
values("0.023, 0.024, 0.037",\
"0.023, 0.024, 0.037",\
"0.024, 0.024, 0.037");
values("0.111, 0.112, 0.115",\
"0.111, 0.111, 0.115",\
"0.111, 0.111, 0.116");
}
}
}
@ -298,19 +302,19 @@ cell (sram_2_16_1_freepdk45){
internal_power(){
when : "!CSb & clk & !WEb";
rise_power(scalar){
values("0.0175059861111");
values("0.027431397222222223");
}
fall_power(scalar){
values("0.0175059861111");
values("0.027431397222222223");
}
}
internal_power(){
when : "!CSb & !clk & WEb";
rise_power(scalar){
values("0.0218644166667");
values("0.026240397222222222");
}
fall_power(scalar){
values("0.0218644166667");
values("0.026240397222222222");
}
}
internal_power(){
@ -326,20 +330,20 @@ cell (sram_2_16_1_freepdk45){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(scalar) {
values("0.117");
values("2.422");
}
fall_constraint(scalar) {
values("0.117");
values("2.422");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("0.234");
values("4.844");
}
fall_constraint(scalar) {
values("0.234");
values("4.844");
}
}
}

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 1032.3999375;
area : 948.52275;
leakage_power () {
when : "CSb";
value : 0.000173;
value : 0.000168;
}
cell_leakage_power : 0;
bus(DATA){
bus(DIN){
bus_type : DATA;
direction : inout;
direction : in;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
memory_read(){
address : ADDR;
}
pin(DATA[1:0]){
pin(DOUT[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk";
@ -130,16 +134,16 @@ cell (sram_2_16_1_freepdk45){
timing(){
timing_sense : non_unate;
related_pin : "clk";
timing_type : falling_edge;
timing_type : rising_edge;
cell_rise(CELL_TABLE) {
values("0.123, 0.124, 0.133",\
"0.123, 0.124, 0.133",\
"0.123, 0.124, 0.133");
values("0.103, 0.104, 0.113",\
"0.103, 0.104, 0.113",\
"0.103, 0.104, 0.113");
}
cell_fall(CELL_TABLE) {
values("0.123, 0.124, 0.133",\
"0.123, 0.124, 0.133",\
"0.123, 0.124, 0.133");
values("0.103, 0.104, 0.113",\
"0.103, 0.104, 0.113",\
"0.103, 0.104, 0.113");
}
rise_transition(CELL_TABLE) {
values("0.006, 0.007, 0.018",\
@ -298,19 +302,19 @@ cell (sram_2_16_1_freepdk45){
internal_power(){
when : "!CSb & clk & !WEb";
rise_power(scalar){
values("0.065526962224");
values("0.0739870044551111");
}
fall_power(scalar){
values("0.065526962224");
values("0.0739870044551111");
}
}
internal_power(){
when : "!CSb & !clk & WEb";
rise_power(scalar){
values("0.065526962224");
values("0.0739870044551111");
}
fall_power(scalar){
values("0.065526962224");
values("0.0739870044551111");
}
}
internal_power(){
@ -336,10 +340,10 @@ cell (sram_2_16_1_freepdk45){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("0.0");
values("0");
}
fall_constraint(scalar) {
values("0.0");
values("0");
}
}
}

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_freepdk45){
dont_use : true;
map_only : true;
dont_touch : true;
area : 1032.3999375;
area : 948.52275;
leakage_power () {
when : "CSb";
value : 0.0008128352;
value : 0.0021292;
}
cell_leakage_power : 0;
bus(DATA){
bus(DIN){
bus_type : DATA;
direction : inout;
direction : in;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
memory_read(){
address : ADDR;
}
pin(DATA[1:0]){
pin(DOUT[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk";
@ -130,26 +134,26 @@ cell (sram_2_16_1_freepdk45){
timing(){
timing_sense : non_unate;
related_pin : "clk";
timing_type : falling_edge;
timing_type : rising_edge;
cell_rise(CELL_TABLE) {
values("0.054, 0.055, 0.061",\
"0.055, 0.056, 0.062",\
"0.06, 0.061, 0.068");
values("0.227, 0.227, 0.231",\
"0.227, 0.228, 0.232",\
"0.233, 0.234, 0.238");
}
cell_fall(CELL_TABLE) {
values("0.066, 0.067, 0.075",\
"0.067, 0.068, 0.076",\
"0.072, 0.073, 0.082");
values("2.555, 2.557, 2.569",\
"2.556, 2.557, 2.569",\
"2.562, 2.563, 2.576");
}
rise_transition(CELL_TABLE) {
values("0.013, 0.014, 0.026",\
"0.013, 0.015, 0.026",\
"0.013, 0.015, 0.026");
values("0.02, 0.021, 0.028",\
"0.02, 0.021, 0.028",\
"0.02, 0.021, 0.028");
}
fall_transition(CELL_TABLE) {
values("0.023, 0.024, 0.037",\
"0.023, 0.024, 0.037",\
"0.024, 0.024, 0.037");
values("0.11, 0.11, 0.114",\
"0.109, 0.11, 0.113",\
"0.11, 0.11, 0.114");
}
}
}
@ -298,19 +302,19 @@ cell (sram_2_16_1_freepdk45){
internal_power(){
when : "!CSb & clk & !WEb";
rise_power(scalar){
values("0.0159801855389");
values("0.025181683333333333");
}
fall_power(scalar){
values("0.0159801855389");
values("0.025181683333333333");
}
}
internal_power(){
when : "!CSb & !clk & WEb";
rise_power(scalar){
values("0.0171325605389");
values("0.024945991666666667");
}
fall_power(scalar){
values("0.0171325605389");
values("0.024945991666666667");
}
}
internal_power(){
@ -326,20 +330,20 @@ cell (sram_2_16_1_freepdk45){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(scalar) {
values("0.1125");
values("2.422");
}
fall_constraint(scalar) {
values("0.1125");
values("2.422");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("0.225");
values("4.844");
}
fall_constraint(scalar) {
values("0.225");
values("4.844");
}
}
}

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 134589.78;
area : 142800.38999999998;
leakage_power () {
when : "CSb";
value : 0.0004764706;
value : 0.0252988;
}
cell_leakage_power : 0;
bus(DATA){
bus(DIN){
bus_type : DATA;
direction : inout;
direction : in;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){
address : ADDR;
}
pin(DATA[1:0]){
pin(DOUT[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk";
@ -130,26 +134,26 @@ cell (sram_2_16_1_scn3me_subm){
timing(){
timing_sense : non_unate;
related_pin : "clk";
timing_type : falling_edge;
timing_type : rising_edge;
cell_rise(CELL_TABLE) {
values("0.474, 0.52, 0.888",\
"0.477, 0.522, 0.892",\
"0.517, 0.561, 0.929");
values("0.945, 0.976, 1.139",\
"0.948, 0.98, 1.143",\
"1.003, 1.036, 1.202");
}
cell_fall(CELL_TABLE) {
values("0.582, 0.658, 1.26",\
"0.586, 0.661, 1.262",\
"0.626, 0.7, 1.298");
values("11.211, 11.266, 11.754",\
"11.212, 11.267, 11.755",\
"11.264, 11.319, 11.806");
}
rise_transition(CELL_TABLE) {
values("0.155, 0.233, 1.087",\
"0.156, 0.235, 1.086",\
"0.16, 0.239, 1.086");
values("0.605, 0.629, 0.98",\
"0.605, 0.629, 0.979",\
"0.604, 0.628, 0.973");
}
fall_transition(CELL_TABLE) {
values("0.277, 0.356, 1.502",\
"0.278, 0.358, 1.501",\
"0.279, 0.363, 1.5");
values("11.17, 11.175, 1.284",\
"11.167, 11.173, 1.284",\
"11.173, 11.179, 11.473");
}
}
}
@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){
when : "!CSb & clk & !WEb";
rise_power(scalar){
values("4.92665");
values("2.1762222222222225");
}
fall_power(scalar){
values("4.92665");
values("2.1762222222222225");
}
}
internal_power(){
when : "!CSb & !clk & WEb";
rise_power(scalar){
values("5.74515833333");
values("2.167955555555556");
}
fall_power(scalar){
values("5.74515833333");
values("2.167955555555556");
}
}
internal_power(){
@ -326,20 +330,20 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(scalar) {
values("1.875");
values("9.6875");
}
fall_constraint(scalar) {
values("1.875");
values("9.6875");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("3.75");
values("19.375");
}
fall_constraint(scalar) {
values("3.75");
values("19.375");
}
}
}

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 134589.78;
area : 142800.38999999998;
leakage_power () {
when : "CSb";
value : 0.000173;
value : 0.000168;
}
cell_leakage_power : 0;
bus(DATA){
bus(DIN){
bus_type : DATA;
direction : inout;
direction : in;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){
address : ADDR;
}
pin(DATA[1:0]){
pin(DOUT[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk";
@ -130,16 +134,16 @@ cell (sram_2_16_1_scn3me_subm){
timing(){
timing_sense : non_unate;
related_pin : "clk";
timing_type : falling_edge;
timing_type : rising_edge;
cell_rise(CELL_TABLE) {
values("0.556, 0.603, 1.044",\
"0.556, 0.603, 1.044",\
"0.556, 0.603, 1.044");
values("0.54, 0.587, 1.028",\
"0.54, 0.587, 1.028",\
"0.54, 0.587, 1.028");
}
cell_fall(CELL_TABLE) {
values("0.556, 0.603, 1.044",\
"0.556, 0.603, 1.044",\
"0.556, 0.603, 1.044");
values("0.54, 0.587, 1.028",\
"0.54, 0.587, 1.028",\
"0.54, 0.587, 1.028");
}
rise_transition(CELL_TABLE) {
values("0.024, 0.081, 0.61",\
@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){
when : "!CSb & clk & !WEb";
rise_power(scalar){
values("10.9314668117");
values("10.559086132533329");
}
fall_power(scalar){
values("10.9314668117");
values("10.559086132533329");
}
}
internal_power(){
when : "!CSb & !clk & WEb";
rise_power(scalar){
values("10.9314668117");
values("10.559086132533329");
}
fall_power(scalar){
values("10.9314668117");
values("10.559086132533329");
}
}
internal_power(){
@ -336,10 +340,10 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("0.0");
values("0");
}
fall_constraint(scalar) {
values("0.0");
values("0");
}
}
}

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true;
map_only : true;
dont_touch : true;
area : 134589.78;
area : 142800.38999999998;
leakage_power () {
when : "CSb";
value : 0.0004764706;
value : 0.0252988;
}
cell_leakage_power : 0;
bus(DATA){
bus(DIN){
bus_type : DATA;
direction : inout;
direction : in;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
three_state : "!OEb & !clk";
memory_write(){
address : ADDR;
clocked_on : clk;
}
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){
address : ADDR;
}
pin(DATA[1:0]){
pin(DOUT[1:0]){
timing(){
timing_type : setup_rising;
related_pin : "clk";
@ -130,26 +134,26 @@ cell (sram_2_16_1_scn3me_subm){
timing(){
timing_sense : non_unate;
related_pin : "clk";
timing_type : falling_edge;
timing_type : rising_edge;
cell_rise(CELL_TABLE) {
values("0.458, 0.504, 0.871",\
"0.461, 0.506, 0.874",\
"0.5, 0.544, 0.912");
values("0.928, 0.959, 1.113",\
"0.931, 0.962, 1.116",\
"0.985, 1.018, 1.176");
}
cell_fall(CELL_TABLE) {
values("0.573, 0.649, 1.251",\
"0.577, 0.652, 1.254",\
"0.618, 0.69, 1.29");
values("11.214, 11.27, 11.756",\
"11.22, 11.27, 11.761",\
"11.268, 11.323, 11.809");
}
rise_transition(CELL_TABLE) {
values("0.153, 0.233, 1.085",\
"0.154, 0.234, 1.084",\
"0.158, 0.237, 1.084");
values("0.599, 0.624, 0.968",\
"0.599, 0.623, 0.97",\
"0.598, 0.623, 0.967");
}
fall_transition(CELL_TABLE) {
values("0.276, 0.356, 1.5",\
"0.277, 0.357, 1.5",\
"0.278, 0.363, 1.5");
values("11.157, 11.165, 11.446",\
"11.159, 11.162, 1.271",\
"11.158, 11.165, 11.47");
}
}
}
@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){
when : "!CSb & clk & !WEb";
rise_power(scalar){
values("4.42361814306");
values("2.033783461111111");
}
fall_power(scalar){
values("4.42361814306");
values("2.033783461111111");
}
}
internal_power(){
when : "!CSb & !clk & WEb";
rise_power(scalar){
values("4.97118480973");
values("2.032705683333334");
}
fall_power(scalar){
values("4.97118480973");
values("2.032705683333334");
}
}
internal_power(){
@ -326,20 +330,20 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"min_pulse_width";
related_pin : clk;
rise_constraint(scalar) {
values("1.875");
values("9.6875");
}
fall_constraint(scalar) {
values("1.875");
values("9.6875");
}
}
timing(){
timing_type :"minimum_period";
related_pin : clk;
rise_constraint(scalar) {
values("3.75");
values("19.375");
}
fall_constraint(scalar) {
values("3.75");
values("19.375");
}
}
}

View File

@ -59,33 +59,78 @@ class openram_test(unittest.TestCase):
# Reset the static duplicate name checker for unit tests.
import hierarchy_design
hierarchy_design.hierarchy_design.name_map=[]
def check_golden_data(self, data, golden_data, error_tolerance=1e-2):
"""
This function goes through two dictionaries, key by key and compares
each item. It uses relative comparisons for the items and returns false
if there is a mismatch.
"""
# Check each result
data_matches = True
for k in data.keys():
if type(data[k])==list:
for i in range(len(data[k])):
if not self.isclose(k,data[k][i],golden_data[k][i],error_tolerance):
data_matches = False
else:
self.isclose(k,data[k],golden_data[k],error_tolerance)
if not data_matches:
import pprint
data_string=pprint.pformat(data)
debug.error("Results exceeded {:.1f}% tolerance compared to golden results:\n".format(error_tolerance*100)+data_string)
return data_matches
def isclose(self, value1,value2,error_tolerance=1e-2):
def isclose(self,key,value,actual_value,error_tolerance=1e-2):
""" This is used to compare relative values. """
import debug
relative_diff = abs(value1 - value2) / max(value1,value2)
relative_diff = self.relative_diff(value,actual_value)
check = relative_diff <= error_tolerance
if not check:
self.fail("NOT CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff))
else:
debug.info(2,"CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff))
def relative_compare(self, value1,value2,error_tolerance):
""" This is used to compare relative values. """
if (value1==value2): # if we don't need a relative comparison!
if check:
debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100))
return True
return (abs(value1 - value2) / max(value1,value2) <= error_tolerance)
else:
debug.error("NOT CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100))
return False
def isapproxdiff(self, f1, f2, error_tolerance=0.001):
def relative_diff(self, value1, value2):
""" Compute the relative difference of two values and normalize to the largest.
If largest value is 0, just return the difference."""
# Edge case to avoid divide by zero
if value1==0 and value2==0:
return 0.0
# Don't need relative, exact compare
if value1==value2:
return 0.0
# Get normalization value
norm_value = abs(max(value1, value2))
# Edge case where greater is a zero
if norm_value == 0:
min_value = abs(min(value1, value2))
return abs(value1 - value2) / norm_value
def relative_compare(self, value,actual_value,error_tolerance):
""" This is used to compare relative values. """
if (value==actual_value): # if we don't need a relative comparison!
return True
return (abs(value - actual_value) / max(value,actual_value) <= error_tolerance)
def isapproxdiff(self, filename1, filename2, error_tolerance=0.001):
"""Compare two files.
Arguments:
f1 -- First file name
filename1 -- First file name
f2 -- Second file name
filename2 -- Second file name
Return value:
@ -106,61 +151,93 @@ class openram_test(unittest.TestCase):
(?: [Ee] [+-]? \d+ ) ?
"""
rx = re.compile(numeric_const_pattern, re.VERBOSE)
with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2:
while True:
b1 = fp1.readline().decode('utf-8')
b2 = fp2.readline().decode('utf-8')
#print "b1:",b1,
#print "b2:",b2,
fp1 = open(filename1, 'rb')
fp2 = open(filename2, 'rb')
mismatches=0
line_num=0
while True:
line_num+=1
line1 = fp1.readline().decode('utf-8')
line2 = fp2.readline().decode('utf-8')
#print("line1:",line1)
#print("line2:",line2)
# 1. Find all of the floats using a regex
line1_floats=rx.findall(line1)
line2_floats=rx.findall(line2)
debug.info(3,"line1_floats: "+str(line1_floats))
debug.info(3,"line2_floats: "+str(line2_floats))
# 1. Find all of the floats using a regex
b1_floats=rx.findall(b1)
b2_floats=rx.findall(b2)
debug.info(3,"b1_floats: "+str(b1_floats))
debug.info(3,"b2_floats: "+str(b2_floats))
# 2. Remove the floats from the string
for f in b1_floats:
b1=b1.replace(f,"",1)
for f in b2_floats:
b2=b2.replace(f,"",1)
#print "b1:",b1,
#print "b2:",b2,
# 3. Check if remaining string matches
if b1 != b2:
self.fail("MISMATCH Line: {0}\n!=\nLine: {1}".format(b1,b2))
# 2. Remove the floats from the string
for f in line1_floats:
line1=line1.replace(f,"",1)
for f in line2_floats:
line2=line2.replace(f,"",1)
#print("line1:",line1)
#print("line2:",line2)
# 4. Now compare that the floats match
if len(b1_floats)!=len(b2_floats):
self.fail("MISMATCH Length {0} != {1}".format(len(b1_floats),len(b2_floats)))
for (f1,f2) in zip(b1_floats,b2_floats):
if not self.relative_compare(float(f1),float(f2),error_tolerance):
self.fail("MISMATCH Float {0} != {1}".format(f1,f2))
# 3. Convert to floats rather than strings
line1_floats = [float(x) for x in line1_floats]
line2_floats = [float(x) for x in line1_floats]
# 4. Check if remaining string matches
if line1 != line2:
if mismatches==0:
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
mismatches += 1
debug.error("MISMATCH Line ({0}):\n{1}\n!=\n{2}".format(line_num,line1.rstrip('\n'),line2.rstrip('\n')))
if not b1 and not b2:
return
# 5. Now compare that the floats match
elif len(line1_floats)!=len(line2_floats):
if mismatches==0:
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
mismatches += 1
debug.error("MISMATCH Line ({0}) Length {1} != {2}".format(line_num,len(line1_floats),len(line2_floats)))
else:
for (float1,float2) in zip(line1_floats,line2_floats):
relative_diff = self.relative_diff(float1,float2)
check = relative_diff <= error_tolerance
if not check:
if mismatches==0:
debug.error("Mismatching files:\nfile1={0}\nfile2={1}".format(filename1,filename2))
mismatches += 1
debug.error("MISMATCH Line ({0}) Float {1} != {2} diff: {3:.1f}%".format(line_num,float1,float2,relative_diff*100))
# Only show the first 10 mismatch lines
if not line1 and not line2 or mismatches>10:
fp1.close()
fp2.close()
return mismatches==0
# Never reached
return False
def isdiff(self,file1,file2):
def isdiff(self,filename1,filename2):
""" This is used to compare two files and display the diff if they are different.. """
import debug
import filecmp
import difflib
check = filecmp.cmp(file1,file2)
check = filecmp.cmp(filename1,filename2)
if not check:
debug.info(2,"MISMATCH {0} {1}".format(file1,file2))
f1 = open(file1,"r")
s1 = f1.readlines()
f2 = open(file2,"r")
debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2))
f1 = open(filename1,"r")
s1 = f1.readlines().decode('utf-8')
f1.close()
f2 = open(filename2,"r").decode('utf-8')
s2 = f2.readlines()
f2.close()
mismatches=0
for line in difflib.unified_diff(s1, s2):
debug.info(3,line)
self.fail("MISMATCH {0} {1}".format(file1,file2))
mismatches += 1
self.error("DIFF LINES:",line)
if mismatches>10:
return False
return False
else:
debug.info(2,"MATCH {0} {1}".format(file1,file2))
debug.info(2,"MATCH {0} {1}".format(filename1,filename2))
return True
def header(filename, technology):
# Skip the header for gitlab regression

View File

@ -1,10 +1,10 @@
.SUBCKT cell_6t bl br wl vdd gnd
MM3 bl wl net10 gnd NMOS_VTG W=135.00n L=50n
MM2 br wl net4 gnd NMOS_VTG W=135.00n L=50n
MM1 net10 net4 gnd gnd NMOS_VTG W=205.00n L=50n
MM0 net4 net10 gnd gnd NMOS_VTG W=205.00n L=50n
MM5 net10 net4 vdd vdd PMOS_VTG W=90n L=50n
MM4 net4 net10 vdd vdd PMOS_VTG W=90n L=50n
MM3 bl wl Q gnd NMOS_VTG W=135.00n L=50n
MM2 br wl Qb gnd NMOS_VTG W=135.00n L=50n
MM1 Q Qb gnd gnd NMOS_VTG W=205.00n L=50n
MM0 Qb Q gnd gnd NMOS_VTG W=205.00n L=50n
MM5 Q Qb vdd vdd PMOS_VTG W=90n L=50n
MM4 Qb Q vdd vdd PMOS_VTG W=90n L=50n
.ENDS cell_6t

View File

@ -1,10 +1,10 @@
*********************** "cell_6t" ******************************
.SUBCKT cell_6t bl br wl vdd gnd
M_1 net_1 net_2 vdd vdd p W='0.9u' L=1.2u
M_2 net_2 net_1 vdd vdd p W='0.9u' L=1.2u
M_3 br wl net_2 gnd n W='1.2u' L=0.6u
M_4 bl wl net_1 gnd n W='1.2u' L=0.6u
M_5 net_2 net_1 gnd gnd n W='2.4u' L=0.6u
M_6 net_1 net_2 gnd gnd n W='2.4u' L=0.6u
M_1 Q Qb vdd vdd p W='0.9u' L=1.2u
M_2 Qb Q vdd vdd p W='0.9u' L=1.2u
M_3 br wl Qb gnd n W='1.2u' L=0.6u
M_4 bl wl Q gnd n W='1.2u' L=0.6u
M_5 Qb Q gnd gnd n W='2.4u' L=0.6u
M_6 Q Qb gnd gnd n W='2.4u' L=0.6u
.ENDS $ cell_6t

View File

@ -2,28 +2,28 @@
.SUBCKT write_driver din bl br en vdd gnd
**** Inverter to conver Data_in to data_in_bar ******
M_1 net_3 din gnd gnd n W='1.2*1u' L=0.6u
M_2 net_3 din vdd vdd p W='2.1*1u' L=0.6u
M_1 din_bar din gnd gnd n W='1.2*1u' L=0.6u
M_2 din_bar din vdd vdd p W='2.1*1u' L=0.6u
**** 2input nand gate follwed by inverter to drive BL ******
M_3 net_2 en net_7 gnd n W='2.1*1u' L=0.6u
M_3 din_bar_gated en net_7 gnd n W='2.1*1u' L=0.6u
M_4 net_7 din gnd gnd n W='2.1*1u' L=0.6u
M_5 net_2 en vdd vdd p W='2.1*1u' L=0.6u
M_6 net_2 din vdd vdd p W='2.1*1u' L=0.6u
M_5 din_bar_gated en vdd vdd p W='2.1*1u' L=0.6u
M_6 din_bar_gated din vdd vdd p W='2.1*1u' L=0.6u
M_7 net_1 net_2 vdd vdd p W='2.1*1u' L=0.6u
M_8 net_1 net_2 gnd gnd n W='1.2*1u' L=0.6u
M_7 net_1 din_bar_gated vdd vdd p W='2.1*1u' L=0.6u
M_8 net_1 din_bar_gated gnd gnd n W='1.2*1u' L=0.6u
**** 2input nand gate follwed by inverter to drive BR******
M_9 net_4 en vdd vdd p W='2.1*1u' L=0.6u
M_10 net_4 en net_8 gnd n W='2.1*1u' L=0.6u
M_11 net_8 net_3 gnd gnd n W='2.1*1u' L=0.6u
M_12 net_4 net_3 vdd vdd p W='2.1*1u' L=0.6u
M_9 din_gated en vdd vdd p W='2.1*1u' L=0.6u
M_10 din_gated en net_8 gnd n W='2.1*1u' L=0.6u
M_11 net_8 din_bar gnd gnd n W='2.1*1u' L=0.6u
M_12 din_gated din_bar vdd vdd p W='2.1*1u' L=0.6u
M_13 net_6 net_4 vdd vdd p W='2.1*1u' L=0.6u
M_14 net_6 net_4 gnd gnd n W='1.2*1u' L=0.6u
M_13 net_6 din_gated vdd vdd p W='2.1*1u' L=0.6u
M_14 net_6 din_gated gnd gnd n W='1.2*1u' L=0.6u
************************************************