mirror of https://github.com/VLSIDA/OpenRAM.git
Merge branch 'dev' of https://github.com/VLSIDA/PrivateRAM into multiport
This commit is contained in:
commit
c2a9e91dba
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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"])
|
||||
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
************************************************
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue