Improved on some hard coded values which determine the measurements.

This commit is contained in:
Hunter Nichols 2018-09-14 18:55:37 -07:00
parent cfe15d48a4
commit 346b188372
1 changed files with 111 additions and 114 deletions

View File

@ -48,8 +48,8 @@ class delay():
self.set_corner(corner) self.set_corner(corner)
self.create_port_names() self.create_port_names()
#Create global measure names. May be an input at some point. Altering the name here will not affect functionality. #Create global measure names. May be an input at some point.
#Removing names will cause program to crash. TODO: This caused me to hardcode indices, fix this to be more dynamic/readable. #Altering the names will crash the characterizer. TODO: object orientated approach to the measurements.
self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"] self.delay_meas_names = ["delay_lh", "delay_hl", "slew_lh", "slew_hl"]
self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"] self.power_meas_names = ["read0_power", "read1_power", "write0_power", "write1_power"]
@ -209,81 +209,91 @@ class delay():
""" """
# Trigger on the clk of the appropriate cycle # Trigger on the clk of the appropriate cycle
trig_name = "clk" trig_clk_name = trig_name = "clk"
#Target name should be an input to the function or a member variable. That way, the ports can be singled out for testing #Target name should be an input to the function or a member variable. That way, the ports can be singled out for testing
targ_name = "{0}".format("DOUT{0}[{1}]".format(port,self.probe_data)) targ_name = "{0}".format("DOUT{0}[{1}]".format(port,self.probe_data))
trig_val = targ_val = 0.5 * self.vdd_voltage trig_val = targ_val = 0.5 * self.vdd_voltage
trig_delay_val = targ_delay_val = 0.5 * self.vdd_voltage
trig_slew_low = 0.1 * self.vdd_voltage
targ_slew_high = 0.9 * self.vdd_voltage
trig_dir = "FALL"
targ_dir = "RISE"
trig_td = targ_td = 0
parse_error = False
# Delay the target to measure after the negative edge # Delay the target to measure after the negative edge
self.stim.gen_meas_delay(meas_name="{0}{1}".format(self.delay_meas_names[1], port), for dname in self.delay_meas_names:
trig_name=trig_name, if 'delay' in dname:
targ_name=targ_name, trig_dir="RISE"
trig_val=trig_val, trig_val = trig_delay_val
targ_val=targ_val, targ_val = targ_val
trig_dir="RISE", trig_name = trig_clk_name
targ_dir="FALL", if 'lh' in dname:
trig_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]], targ_dir="RISE"
targ_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]) else:
targ_dir="FALL"
self.stim.gen_meas_delay(meas_name="{0}{1}".format(self.delay_meas_names[0], port),
trig_name=trig_name, elif 'slew' in dname:
targ_name=targ_name, trig_name = targ_name
trig_val=trig_val, if 'lh' in dname:
targ_val=targ_val, trig_val = trig_slew_low
trig_dir="RISE", targ_val = targ_slew_high
targ_dir="RISE", targ_dir = trig_dir = "RISE"
trig_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]], else:
targ_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]) trig_val = targ_slew_high
targ_val = trig_slew_low
self.stim.gen_meas_delay(meas_name="{0}{1}".format(self.delay_meas_names[3], port), targ_dir = trig_dir = "FALL"
trig_name=targ_name, else:
targ_name=targ_name, debug.error(1, "Measure command {0} not recognized".format(dname))
trig_val=0.9*self.vdd_voltage,
targ_val=0.1*self.vdd_voltage, if 'lh' in dname:
trig_dir="FALL", trig_td = targ_td = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]
targ_dir="FALL", elif 'hl' in dname:
trig_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]], trig_td = targ_td = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]
targ_td=self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]) else:
debug.error(1, "Measure command {0} does not contain direction (lh/hl)".format(dname))
self.stim.gen_meas_delay(meas_name="{0}{1}".format(self.delay_meas_names[2], port),
trig_name=targ_name, self.stim.gen_meas_delay(meas_name="{0}{1}".format(dname, port),
targ_name=targ_name, trig_name=trig_name,
trig_val=0.1*self.vdd_voltage, targ_name=targ_name,
targ_val=0.9*self.vdd_voltage, trig_val=trig_val,
trig_dir="RISE", targ_val=targ_val,
targ_dir="RISE", trig_dir=trig_dir,
trig_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]], targ_dir=targ_dir,
targ_td=self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]) trig_td=trig_td,
targ_td=targ_td)
# add measure statements for power # add measure statements for power
t_initial = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]] for pname in self.power_meas_names:
t_final = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]+1] if "read" not in pname:
self.stim.gen_meas_power(meas_name="{0}{1}".format(self.power_meas_names[0], port), continue
t_initial=t_initial, t_initial = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]]
t_final=t_final) t_final = self.cycle_times[self.measure_cycles["read0_{0}".format(port)]+1]
if '1' in pname:
t_initial = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]] t_initial = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]]
t_final = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]+1] t_final = self.cycle_times[self.measure_cycles["read1_{0}".format(port)]+1]
self.stim.gen_meas_power(meas_name="{0}{1}".format(self.power_meas_names[1], port),
t_initial=t_initial,
t_final=t_final)
self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port),
t_initial=t_initial,
t_final=t_final)
def write_delay_measures_write_port(self, port): def write_delay_measures_write_port(self, port):
""" """
Write the measure statements to quantify the power results for a write port. Write the measure statements to quantify the power results for a write port.
""" """
# add measure statements for power # add measure statements for power
t_initial = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]] for pname in self.power_meas_names:
t_final = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]+1] if "write" not in pname:
self.stim.gen_meas_power(meas_name="{0}{1}".format(self.power_meas_names[2], port), continue
t_initial=t_initial, t_initial = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]]
t_final=t_final) t_final = self.cycle_times[self.measure_cycles["write0_{0}".format(port)]+1]
if '1' in pname:
t_initial = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]] t_initial = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]]
t_final = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]+1] t_final = self.cycle_times[self.measure_cycles["write1_{0}".format(port)]+1]
self.stim.gen_meas_power(meas_name="{0}{1}".format(self.power_meas_names[3], port),
t_initial=t_initial, self.stim.gen_meas_power(meas_name="{0}{1}".format(pname, port),
t_final=t_final) t_initial=t_initial,
t_final=t_final)
def write_delay_measures(self): def write_delay_measures(self):
""" """
@ -351,10 +361,10 @@ class delay():
break break
#Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews #Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews
feasible_delay_measures = [results["{0}{1}".format(mname,port)] for mname in self.delay_meas_names] feasible_delays = [results["{0}{1}".format(mname,port)] for mname in self.delay_meas_names if "delay" in mname]
feasible_slews = [results["{0}{1}".format(mname,port)] for mname in self.delay_meas_names if "slew" in mname]
delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(feasible_delay_measures[0], feasible_delay_measures[1]) delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(*feasible_delays)
slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(feasible_delay_measures[2], feasible_delay_measures[3]) slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(*feasible_slews)
debug.info(2, "feasible_period passed for Port {3}: {0}ns {1} {2} ".format(feasible_period, debug.info(2, "feasible_period passed for Port {3}: {0}ns {1} {2} ".format(feasible_period,
delay_str, delay_str,
slew_str, slew_str,
@ -363,19 +373,18 @@ class delay():
if success: if success:
debug.info(1, "Found feasible_period: {0}ns".format(feasible_period)) debug.info(1, "Found feasible_period: {0}ns".format(feasible_period))
self.period = feasible_period self.period = feasible_period
return (feasible_delay_measures[0], feasible_delay_measures[1]) return results
def find_feasible_period(self): def find_feasible_period(self):
""" """
Loops through all read ports determining the feasible period and collecting Loops through all read ports determining the feasible period and collecting
delay information from each port. delay information from each port.
""" """
feasible_delays_lh = {} feasible_delays = {}
feasible_delays_hl = {}
self.period = float(tech.spice["feasible_period"]) self.period = float(tech.spice["feasible_period"])
#Get initial feasible period from first port #Get initial feasible period from first port
(feasible_delays_lh[0], feasible_delays_hl[0]) = self.find_feasible_period_one_port(self.read_ports[0]) feasible_delays.update(self.find_feasible_period_one_port(self.read_ports[0]))
previous_period = self.period previous_period = self.period
@ -384,14 +393,14 @@ class delay():
i = 1 i = 1
while i < len(self.read_ports): while i < len(self.read_ports):
port = self.read_ports[i] port = self.read_ports[i]
(feasible_delays_lh[port], feasible_delays_hl[port]) = self.find_feasible_period_one_port(port) feasible_delays.update(self.find_feasible_period_one_port(port))
#Function sets the period. Restart the entire process if period changes to collect accurate delays #Function sets the period. Restart the entire process if period changes to collect accurate delays
if self.period > previous_period: if self.period > previous_period:
i = 0 i = 0
else: else:
i+=1 i+=1
previous_period = self.period previous_period = self.period
return (feasible_delays_lh, feasible_delays_hl) return feasible_delays
def parse_values(self, values_names, mult = 1.0): def parse_values(self, values_names, mult = 1.0):
@ -430,11 +439,11 @@ class delay():
for port in self.targ_read_ports: for port in self.targ_read_ports:
delay_names = ["{0}{1}".format(mname,port) for mname in self.delay_meas_names] delay_names = ["{0}{1}".format(mname,port) for mname in self.delay_meas_names]
delays = self.parse_values(delay_names, 1e9) # scale delays to ns delays = self.parse_values(delay_names, 1e9) # scale delays to ns
if not self.check_valid_delays((delays[delay_names[0]],delays[delay_names[1]],delays[delay_names[2]],delays[delay_names[3]])): if not self.check_valid_delays(tuple(delays.values())):
return (False,{}) return (False,{})
result.update(delays) result.update(delays)
power_names = ["{0}{1}".format(mname,port) for mname in self.power_meas_names[:2]] power_names = ["{0}{1}".format(mname,port) for mname in self.power_meas_names if 'read' in mname]
powers = self.parse_values(power_names, 1e3) # scale power to mw powers = self.parse_values(power_names, 1e3) # scale power to mw
#Check that power parsing worked. #Check that power parsing worked.
for name, power in powers.items(): for name, power in powers.items():
@ -443,7 +452,7 @@ class delay():
result.update(powers) result.update(powers)
for port in self.targ_write_ports: for port in self.targ_write_ports:
power_names = ["{0}{1}".format(mname,port) for mname in self.power_meas_names[2:]] power_names = ["{0}{1}".format(mname,port) for mname in self.power_meas_names if 'write' in mname]
powers = self.parse_values(power_names, 1e3) # scale power to mw powers = self.parse_values(power_names, 1e3) # scale power to mw
#Check that power parsing worked. #Check that power parsing worked.
for name, power in powers.items(): for name, power in powers.items():
@ -508,7 +517,7 @@ class delay():
return True return True
def find_min_period(self, feasible_delays_lh, feasible_delays_hl): def find_min_period(self, feasible_delays):
""" """
Determine the minimum period for all ports. Determine the minimum period for all ports.
""" """
@ -520,7 +529,7 @@ class delay():
#Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position. #Find the minimum period for all ports. Start at one port and perform binary search then use that delay as a starting position.
#For testing purposes, only checks read ports. #For testing purposes, only checks read ports.
for port in self.read_ports: for port in self.read_ports:
target_period = self.find_min_period_one_port(feasible_delays_lh, feasible_delays_hl, port, lb_period, ub_period, target_period) target_period = self.find_min_period_one_port(feasible_delays, port, lb_period, ub_period, target_period)
#The min period of one port becomes the new lower bound. Reset the upper_bound. #The min period of one port becomes the new lower bound. Reset the upper_bound.
lb_period = target_period lb_period = target_period
ub_period = feasible_period ub_period = feasible_period
@ -530,7 +539,7 @@ class delay():
self.targ_write_ports = [] self.targ_write_ports = []
return target_period return target_period
def find_min_period_one_port(self, feasible_delays_lh, feasible_delays_hl, port, lb_period, ub_period, target_period): def find_min_period_one_port(self, feasible_delays, port, lb_period, ub_period, target_period):
""" """
Searches for the smallest period with output delays being within 5% of Searches for the smallest period with output delays being within 5% of
long period. For the current logic to characterize multiport, bound are required as an input. long period. For the current logic to characterize multiport, bound are required as an input.
@ -555,24 +564,20 @@ class delay():
lb_period, lb_period,
port)) port))
if self.try_period(feasible_delays_lh, feasible_delays_hl): if self.try_period(feasible_delays):
ub_period = target_period ub_period = target_period
else: else:
lb_period = target_period lb_period = target_period
if relative_compare(ub_period, lb_period, error_tolerance=0.05): if relative_compare(ub_period, lb_period, error_tolerance=0.05):
# ub_period is always feasible. When done with a port, set the target period of the next port as the lower bound # ub_period is always feasible.
# and reset the upperbound
return ub_period return ub_period
#target_period = lb_period = ub_period
#ub_period = previous_period
#break
#Update target #Update target
target_period = 0.5 * (ub_period + lb_period) target_period = 0.5 * (ub_period + lb_period)
def try_period(self, feasible_delays_lh, feasible_delays_hl): def try_period(self, feasible_delays):
""" """
This tries to simulate a period and checks if the result This tries to simulate a period and checks if the result
works. If it does and the delay is within 5% still, it returns True. works. If it does and the delay is within 5% still, it returns True.
@ -583,25 +588,20 @@ class delay():
return False return False
#Check the values of target readwrite and read ports. Write ports do not produce delays in this current version #Check the values of target readwrite and read ports. Write ports do not produce delays in this current version
for port in self.targ_read_ports: for port in self.targ_read_ports:
#Positions of measurements currently hardcoded. First 2 are delays, next 2 are slews delay_port_names = ["{0}{1}".format(mname,port) for mname in self.delay_meas_names if "delay" in mname]
delay_measures = [results["{0}{1}".format(mname,port)] for mname in self.delay_meas_names] for dname in delay_port_names:
if not relative_compare(results[dname],feasible_delays[dname],error_tolerance=0.05):
if not relative_compare(delay_measures[0],feasible_delays_lh[port],error_tolerance=0.05): debug.info(2,"Delay too big {0} vs {1}".format(results[dname],feasible_delays[dname]))
debug.info(2,"Delay too big {0} vs {1}".format(delay_measures[0],feasible_delays_lh[port])) return False
return False
elif not relative_compare(delay_measures[1],feasible_delays_hl[port],error_tolerance=0.05):
debug.info(2,"Delay too big {0} vs {1}".format(delay_measures[1],feasible_delays_hl[port]))
return False
#key=raw_input("press return to continue") #key=raw_input("press return to continue")
debug.info(2,"Successful period {0}, Port {5}, delay_lh={1}ns, delay_hl={2}ns, slew_lh={3}ns slew_hl={4}ns".format(self.period, #Dynamic way to build string. A bit messy though.
delay_measures[0], delay_str = ', '.join("{0}={1}ns".format(mname, results["{0}{1}".format(mname,port)]) for mname in self.delay_meas_names)
delay_measures[1], debug.info(2,"Successful period {0}, Port {2}, {1}".format(self.period,
delay_measures[2], delay_str,
delay_measures[3], port))
port))
return True return True
def set_probe(self,probe_address, probe_data): def set_probe(self,probe_address, probe_data):
@ -670,17 +670,14 @@ class delay():
# return char_data # return char_data
# 1) Find a feasible period and it's corresponding delays using the trimmed array. # 1) Find a feasible period and it's corresponding delays using the trimmed array.
(feasible_delays_lh, feasible_delays_hl) = self.find_feasible_period() feasible_delays = self.find_feasible_period()
#Check all the delays #Check all the delays
for k,v in feasible_delays_lh.items(): for k,v in feasible_delays.items():
debug.check(v>0,"Negative delay may not be possible") debug.check(v>0,"Negative delay may not be possible: {0}={1}".format(k,v))
for k,v in feasible_delays_hl.items():
debug.check(v>0,"Negative delay may not be possible")
# 2) Finds the minimum period without degrading the delays by X% # 2) Finds the minimum period without degrading the delays by X%
self.set_load_slew(max(loads),max(slews)) self.set_load_slew(max(loads),max(slews))
min_period = self.find_min_period(feasible_delays_lh, feasible_delays_hl) min_period = self.find_min_period(feasible_delays)
debug.check(type(min_period)==float,"Couldn't find minimum period.") debug.check(type(min_period)==float,"Couldn't find minimum period.")
debug.info(1, "Min Period Found: {0}ns".format(min_period)) debug.info(1, "Min Period Found: {0}ns".format(min_period))
char_data["min_period"] = round_time(min_period) char_data["min_period"] = round_time(min_period)