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, def create_channel_route(self, route_map, top_pins, bottom_pins, offset,
layer_stack=("metal1", "via1", "metal2"), pitch=None, layer_stack=("metal1", "via1", "metal2"), pitch=None,
vertical=False): vertical=False):
""" """
This is a simple channel route for one-to-one connections that 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 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 top_name,top_pin in top_pins.items():
for bot_name,bot_pin in bottom_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: 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[top_name].append(bot_name)
vcg[bot_name].append(top_name)
elif vertical and abs(top_pin.center().y-bot_pin.center().y) < pitch: 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[top_name].append(bot_name)
vcg[bot_name].append(top_name)
# This is the starting offset of the first trunk # This is the starting offset of the first trunk
if vertical: if vertical:

View File

@ -89,7 +89,7 @@ class delay():
self.check_arguments() self.check_arguments()
# obtains list of time-points for each rising clk edge # 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 # creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
@ -105,25 +105,13 @@ class delay():
# generate data and addr signals # generate data and addr signals
self.sf.write("\n* Generation of data and address signals\n") self.sf.write("\n* Generation of data and address signals\n")
for i in range(self.word_size): self.gen_data()
if i == self.probe_data: self.gen_addr()
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)
# generate control signals # generate control signals
self.sf.write("\n* Generation of control signals\n") self.sf.write("\n* Generation of control signals\n")
self.gen_csb(self.cycle_times) self.gen_control()
self.gen_web(self.cycle_times)
self.gen_oeb(self.cycle_times)
self.sf.write("\n* Generation of global clock signal\n") self.sf.write("\n* Generation of global clock signal\n")
self.stim.gen_pulse(sig_name="CLK", self.stim.gen_pulse(sig_name="CLK",
@ -149,13 +137,13 @@ class delay():
self.check_arguments() self.check_arguments()
# obtains list of time-points for each rising clk edge # 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 # creates and opens stimulus file for writing
temp_stim = "{0}/stim.sp".format(OPTS.openram_temp) temp_stim = "{0}/stim.sp".format(OPTS.openram_temp)
self.sf = open(temp_stim, "w") self.sf = open(temp_stim, "w")
self.sf.write("* Power stimulus for period of {0}n\n\n".format(self.period)) 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 # include UNTRIMMED files in stimulus file
if trim: if trim:
@ -213,20 +201,20 @@ class delay():
targ_name=targ_name, targ_name=targ_name,
trig_val=trig_val, trig_val=trig_val,
targ_val=targ_val, targ_val=targ_val,
trig_dir="FALL", trig_dir="RISE",
targ_dir="FALL", targ_dir="FALL",
trig_td=self.cycle_times[self.read0_cycle], 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", self.stim.gen_meas_delay(meas_name="DELAY_LH",
trig_name=trig_name, trig_name=trig_name,
targ_name=targ_name, targ_name=targ_name,
trig_val=trig_val, trig_val=trig_val,
targ_val=targ_val, targ_val=targ_val,
trig_dir="FALL", trig_dir="RISE",
targ_dir="RISE", targ_dir="RISE",
trig_td=self.cycle_times[self.read1_cycle], 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", self.stim.gen_meas_delay(meas_name="SLEW_HL",
trig_name=targ_name, trig_name=targ_name,
@ -236,7 +224,7 @@ class delay():
trig_dir="FALL", trig_dir="FALL",
targ_dir="FALL", targ_dir="FALL",
trig_td=self.cycle_times[self.read0_cycle], 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", self.stim.gen_meas_delay(meas_name="SLEW_LH",
trig_name=targ_name, trig_name=targ_name,
@ -246,7 +234,7 @@ class delay():
trig_dir="RISE", trig_dir="RISE",
targ_dir="RISE", targ_dir="RISE",
trig_td=self.cycle_times[self.read1_cycle], 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 # add measure statements for power
t_initial = self.cycle_times[self.write0_cycle] t_initial = self.cycle_times[self.write0_cycle]
@ -315,11 +303,11 @@ class delay():
feasible_delay_hl = results["delay_hl"] feasible_delay_hl = results["delay_hl"]
feasible_slew_hl = results["slew_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, delay_str = "feasible_delay {0:.4f}ns/{1:.4f}ns".format(feasible_delay_lh, feasible_delay_hl)
feasible_delay_lh, slew_str = "slew {0:.4f}ns/{1:.4f}ns".format(feasible_slew_lh, feasible_slew_hl)
feasible_delay_hl, debug.info(1, "Found feasible_period: {0}ns {1} {2} ".format(feasible_period,
feasible_slew_lh, delay_str,
feasible_slew_hl)) slew_str))
self.period = feasible_period self.period = feasible_period
return (feasible_delay_lh, feasible_delay_hl) return (feasible_delay_lh, feasible_delay_hl)
@ -392,39 +380,32 @@ class delay():
""" Check if the measurements are defined and if they are valid. """ """ Check if the measurements are defined and if they are valid. """
(delay_hl, delay_lh, slew_hl, slew_lh) = delay_tuple (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 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: 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, delays_str = "delay_hl={0} delay_lh={1}".format(delay_hl, delay_lh)
self.load, slews_str = "slew_hl={0} slew_lh={1}".format(slew_hl,slew_lh)
self.slew, debug.info(2,"Failed simulation (in sec):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
delay_hl, delays_str,
delay_lh, slews_str))
slew_hl,
slew_lh))
return False return False
# Scale delays to ns (they previously could have not been floats) # Scale delays to ns (they previously could have not been floats)
delay_hl *= 1e9 delay_hl *= 1e9
delay_lh *= 1e9 delay_lh *= 1e9
slew_hl *= 1e9 slew_hl *= 1e9
slew_lh *= 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: 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, debug.info(2,"UNsuccessful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
self.load, delays_str,
self.slew, slews_str))
delay_hl,
delay_lh,
slew_hl,
slew_lh))
return False return False
else: 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, debug.info(2,"Successful simulation (in ns):\n\t\t{0}\n\t\t{1}\n\t\t{2}".format(period_load_slew_str,
self.load, delays_str,
self.slew, slews_str))
delay_hl,
delay_lh,
slew_hl,
slew_lh))
return True return True
@ -547,6 +528,8 @@ class delay():
""" """
Main function to characterize an SRAM for a table. Computes both delay and power characterization. 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) 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_lh>0,"Negative delay may not be possible")
debug.check(feasible_delay_hl>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 # 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", for m in ["delay_lh", "delay_hl", "slew_lh", "slew_hl", "read0_power",
"read1_power", "write0_power", "write1_power", "leakage_power"]: "read1_power", "write0_power", "write1_power", "leakage_power"]:
char_data[m]=[] 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() (full_array_leakage, trim_array_leakage)=self.run_power_simulation()
char_data["leakage_power"]=full_array_leakage 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 slew in slews:
for load in loads: for load in loads:
self.set_load_slew(load,slew) 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() (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)) 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(): 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 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) """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 of the cycles to do a timing evaluation. The last time is the end of the simulation
and does not need a rising edge.""" 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_comments = []
self.cycle_times = [] self.cycle_times = []
t_current = 0
# idle cycle, no operation # Control logic signals each cycle
msg = "Idle cycle (no clock)" self.web_values = []
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(0, self.oeb_values = []
t_current, self.csb_values = []
msg))
self.cycle_times.append(t_current)
t_current += self.period
# One period # Address and data values for each address/data bit
msg = "W data 1 address 11..11 to initialize cell" self.data_values=[]
self.cycle_times.append(t_current) for i in range(self.word_size):
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, self.data_values.append([])
t_current, self.addr_values=[]
msg)) for i in range(self.addr_size):
t_current += self.period self.addr_values.append([])
# One period # Create the inverse address for a scratch address
msg = "W data 0 address 11..11 (to ensure a write of value works)" inverse_address = ""
self.cycle_times.append(t_current) for c in self.probe_address:
self.write0_cycle=len(self.cycle_times)-1 if c=="0":
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, inverse_address += "1"
t_current, elif c=="1":
msg)) inverse_address += "0"
t_current += self.period 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 self.add_noop("Idle cycle (no positive clock edge)",
msg = "W data 1 address 00..00 (to clear bus caps)" inverse_address, data_zeros)
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
# One period self.add_write("W data 1 address 0..00",
msg = "R data 0 address 11..11 to check W0 worked" inverse_address,data_ones)
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
# One period self.add_write("W data 0 address 11..11 to write value",
msg = "Idle cycle (Read addr 00..00)" self.probe_address,data_zeros)
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, self.write0_cycle=len(self.cycle_times)-1 # Remember for power measure
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
# One period # This also ensures we will have a H->L transition on the next read
msg = "R data 1 address 11..11 to check W1 worked" self.add_read("R data 1 address 00..00 to set DOUT caps",
self.cycle_times.append(t_current) inverse_address,data_zeros)
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
# One period self.add_read("R data 0 address 11..11 to check W0 worked",
msg = "Idle cycle (Read addr 11..11)" self.probe_address,data_zeros)
self.cycle_comments.append("Cycle{0}\t{1}ns:\t{2}".format(len(self.cycle_times)-1, self.read0_cycle=len(self.cycle_times)-1 # Remember for power measure
t_current,
msg)) self.add_noop("Idle cycle (if read takes >1 cycle)",
self.cycle_times.append(t_current) inverse_address,data_zeros)
t_current += self.period 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 return data
def gen_data(self, clk_times, sig_name): def gen_data(self):
""" Generates the PWL data inputs for a simulation timing test. """ """ Generates the PWL data inputs for a simulation timing test. """
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP for i in range(self.word_size):
# we are asserting the opposite value on the other side of the tx gate during sig_name="DIN[{0}]".format(i)
# the read to be "worst case". Otherwise, it can actually assist the read. self.stim.gen_pwl(sig_name, self.cycle_times, self.data_values[i], self.period, self.slew, 0.05)
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)
def gen_addr(self, clk_times, addr): def gen_addr(self):
""" """
Generates the address inputs for a simulation timing test. Generates the address inputs for a simulation timing test.
This alternates between all 1's and all 0's for the address. This alternates between all 1's and all 0's for the address.
""" """
for i in range(self.addr_size):
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)):
sig_name = "A[{0}]".format(i) sig_name = "A[{0}]".format(i)
if addr[i]=="1": self.stim.gen_pwl(sig_name, self.cycle_times, self.addr_values[i], self.period, self.slew, 0.05)
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)
def gen_csb(self, clk_times): def gen_control(self):
""" Generates the PWL CSb signal """ """ Generates the control signals """
# values for NOP, W1, W0, W1, R0, NOP, W1, W0, R1, NOP self.stim.gen_pwl("csb", self.cycle_times, self.csb_values, self.period, self.slew, 0.05)
# Keep CSb asserted in NOP for measuring >1 period self.stim.gen_pwl("web", self.cycle_times, self.web_values, self.period, self.slew, 0.05)
values = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] self.stim.gen_pwl("oeb", self.cycle_times, self.oeb_values, self.period, self.slew, 0.05)
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)

View File

@ -300,28 +300,34 @@ class lib:
def write_data_bus(self): def write_data_bus(self):
""" Adds data bus timing results.""" """ 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(" 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. # This is conservative, but limit to range that we characterized.
self.lib.write(" max_capacitance : {0}; \n".format(max(self.loads))) 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(" 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(" memory_write(){ \n")
self.lib.write(" address : ADDR; \n") self.lib.write(" address : ADDR; \n")
self.lib.write(" clocked_on : clk; \n") self.lib.write(" clocked_on : clk; \n")
self.lib.write(" }\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(" memory_read(){ \n")
self.lib.write(" address : ADDR; \n") self.lib.write(" address : ADDR; \n")
self.lib.write(" }\n") self.lib.write(" }\n")
self.lib.write(" pin(DOUT[{0}:0]){{\n".format(self.sram.word_size - 1))
self.lib.write(" pin(DATA[{0}:0]){{\n".format(self.sram.word_size - 1))
self.write_FF_setuphold() self.write_FF_setuphold()
self.lib.write(" timing(){ \n") self.lib.write(" timing(){ \n")
self.lib.write(" timing_sense : non_unate; \n") self.lib.write(" timing_sense : non_unate; \n")
self.lib.write(" related_pin : \"clk\"; \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.lib.write(" cell_rise(CELL_TABLE) {\n")
self.write_values(self.char_results["delay_lh"],len(self.loads)," ") self.write_values(self.char_results["delay_lh"],len(self.loads)," ")
self.lib.write(" }\n") # rise delay self.lib.write(" }\n") # rise delay
@ -374,7 +380,7 @@ class lib:
self.lib.write(" pin(clk){\n") self.lib.write(" pin(clk){\n")
self.lib.write(" clock : true;\n") self.lib.write(" clock : true;\n")
self.lib.write(" direction : input; \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"])) 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 # 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 times = np.array(clk_times) - setup*period
values = np.array(data_values) * self.voltage values = np.array(data_values) * self.voltage
half_slew = 0.5 * slew 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])) self.sf.write("V{0} {0} 0 PWL (0n {1}v ".format(sig_name, values[0]))
for i in range(1,len(times)): for i in range(1,len(times)):
self.sf.write("{0}n {1}v {2}n {3}v ".format(times[i]-half_slew, 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 col_address = 0
# 1. Keep cells in the bitcell array based on WL and BL # 1. Keep cells in the bitcell array based on WL and BL
wl_name = "wl[{}]".format(wl_address) 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 # Prepend info about the trimming
addr_msg = "Keeping {} address".format(address) addr_msg = "Keeping {} address".format(address)

View File

@ -61,7 +61,9 @@ class control_logic(design.design):
self.add_mod(self.nand3) self.add_mod(self.nand3)
# Special gates: inverters for buffering # 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.add_mod(self.clkbuf)
self.inv = self.inv1 = pinv(size=1, height=dff_height) self.inv = self.inv1 = pinv(size=1, height=dff_height)
self.add_mod(self.inv1) 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 x_off = self.ctrl_dff_inst.width + self.internal_bus_width
(y_off,mirror)=self.get_offset(row) (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) w_en_bar_offset = vector(x_off, y_off)
self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar", self.w_en_bar_inst=self.add_inst(name="nand3_w_en_bar",
mod=self.nand3, mod=self.nand3,

View File

@ -20,6 +20,12 @@ class dff_buf(design.design):
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(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 from importlib import reload
c = reload(__import__(OPTS.dff)) c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, 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. 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=="": if name=="":
name = "dff_inv_{0}".format(inv_size) name = "dff_inv_{0}".format(inv_size)
design.design.__init__(self, name) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(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 from importlib import reload
c = reload(__import__(OPTS.dff)) c = reload(__import__(OPTS.dff))
self.mod_dff = getattr(c, OPTS.dff) self.mod_dff = getattr(c, OPTS.dff)
self.dff = self.mod_dff("dff") self.dff = self.mod_dff("dff")
self.add_mod(self.dff) self.add_mod(self.dff)
self.inv1 = pinv(size=inv_size,height=self.dff.height) self.inv1 = pinv(size=inv_size,height=self.dff.height)
self.add_mod(self.inv1) self.add_mod(self.inv1)

View File

@ -127,14 +127,14 @@ class wordline_driver(design.design):
mirror=inst_mirror)) mirror=inst_mirror))
self.connect_inst(["en_bar[{0}]".format(row), self.connect_inst(["en_bar[{0}]".format(row),
"in[{0}]".format(row), "in[{0}]".format(row),
"net[{0}]".format(row), "wl_bar[{0}]".format(row),
"vdd", "gnd"]) "vdd", "gnd"])
# add inv2 # add inv2
self.inv2_inst.append(self.add_inst(name=name_inv2, self.inv2_inst.append(self.add_inst(name=name_inv2,
mod=self.inv, mod=self.inv,
offset=inv2_offset, offset=inv2_offset,
mirror=inst_mirror)) mirror=inst_mirror))
self.connect_inst(["net[{0}]".format(row), self.connect_inst(["wl_bar[{0}]".format(row),
"wl[{0}]".format(row), "wl[{0}]".format(row),
"vdd", "gnd"]) "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"] 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"] 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 # Determine the number of mults for each to fit width into available space
self.nmos_width = self.nmos_size*drc["minwidth_tx"] self.nmos_width = self.nmos_size*drc["minwidth_tx"]

View File

@ -15,20 +15,35 @@ class pinvbuf(design.design):
c = reload(__import__(OPTS.bitcell)) c = reload(__import__(OPTS.bitcell))
bitcell = getattr(c, 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=="": 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) design.design.__init__(self, name)
debug.info(1, "Creating {}".format(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.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.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.add_mod(self.inv2)
self.width = 2*self.inv1.width + self.inv2.width self.width = 2*self.inv1.width + self.inv2.width

View File

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

View File

@ -41,7 +41,7 @@ class timing_sram_test(openram_test):
probe_address = "1" * s.s.addr_size probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1 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]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner) d = delay(s.s, tempspice, corner)
@ -49,41 +49,36 @@ class timing_sram_test(openram_test):
loads = [tech.spice["msflop_in_cap"]*4] loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data, slews, loads) data = d.analyze(probe_address, probe_data, slews, loads)
#print data
if OPTS.tech_name == "freepdk45": if OPTS.tech_name == "freepdk45":
golden_data = {'leakage_power': 0.0006964536000000001, golden_data = {'delay_hl': [2.5614],
'delay_lh': [0.0573055], 'delay_lh': [0.22929839999999999],
'read0_power': [0.0337812], 'leakage_power': 0.0020326,
'read1_power': [0.032946500000000004], 'min_period': 4.844,
'write1_power': [0.0361529], 'read0_power': [0.0497676],
'write0_power': [0.026179099999999997], 'read1_power': [0.0463576],
'slew_hl': [0.0285185], 'slew_hl': [0.1119293],
'min_period': 0.205, 'slew_lh': [0.0237043],
'delay_hl': [0.070554], 'write0_power': [0.0494321],
'slew_lh': [0.0190073]} 'write1_power': [0.0457268]}
elif OPTS.tech_name == "scn3me_subm": elif OPTS.tech_name == "scn3me_subm":
golden_data = {'leakage_power': 0.0004004581, golden_data = {'delay_hl': [6.0052],
'delay_lh': [0.6538954], 'delay_lh': [2.2886],
'read0_power': [9.7622], 'leakage_power': 0.025629199999999998,
'read1_power': [9.589], 'min_period': 9.375,
'write1_power': [10.8], 'read0_power': [8.8721],
'write0_power': [6.928400000000001], 'read1_power': [8.3179],
'slew_hl': [0.8321625], 'slew_hl': [1.0746],
'min_period': 2.344, 'slew_lh': [0.413426],
'delay_hl': [0.9019090999999999], 'write0_power': [8.6601],
'slew_lh': [0.5896232]} 'write1_power': [8.0397]}
else: else:
self.assertTrue(False) # other techs fail self.assertTrue(False) # other techs fail
# Check if no too many or too few results # Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys())) 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() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # 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 # Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys())) self.assertTrue(len(data.keys())==len(golden_data.keys()))
# Check each result
for k in data.keys(): self.assertTrue(self.check_golden_data(data,golden_data,0.25))
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)
globals.end_openram() 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)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.spice_name="ngspice" OPTS.spice_name="ngspice"
OPTS.analytical_delay = False OPTS.analytical_delay = False
OPTS.trim_netlist = False
# This is a hack to reload the characterizer __init__ with the spice version # This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload from importlib import reload
@ -39,7 +40,7 @@ class timing_sram_test(openram_test):
probe_address = "1" * s.s.addr_size probe_address = "1" * s.s.addr_size
probe_data = s.s.word_size - 1 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]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay(s.s, tempspice, corner) d = delay(s.s, tempspice, corner)
@ -47,41 +48,36 @@ class timing_sram_test(openram_test):
loads = [tech.spice["msflop_in_cap"]*4] loads = [tech.spice["msflop_in_cap"]*4]
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
data = d.analyze(probe_address, probe_data, slews, loads) data = d.analyze(probe_address, probe_data, slews, loads)
#print data
if OPTS.tech_name == "freepdk45": if OPTS.tech_name == "freepdk45":
golden_data = {'leakage_power': 0.0007348262, golden_data = {'delay_hl': [2.562671],
'delay_lh': [0.05799613], 'delay_lh': [0.2320771],
'read0_power': [0.0384102], 'leakage_power': 0.00102373,
'read1_power': [0.03279848], 'min_period': 4.844,
'write1_power': [0.03693655], 'read0_power': [0.047404110000000006],
'write0_power': [0.02717752], 'read1_power': [0.0438884],
'slew_hl': [0.03607912], 'slew_hl': [0.1140206],
'min_period': 0.742, 'slew_lh': [0.02492785],
'delay_hl': [0.3929995], 'write0_power': [0.04765188],
'slew_lh': [0.02160862]} 'write1_power': [0.04434999]}
elif OPTS.tech_name == "scn3me_subm": elif OPTS.tech_name == "scn3me_subm":
golden_data = {'leakage_power': 0.00142014, golden_data = {'delay_hl': [11.69536],
'delay_lh': [0.8018421], 'delay_lh': [1.260921],
'read0_power': [11.44908], 'leakage_power': 0.00039469710000000004,
'read1_power': [11.416549999999999], 'min_period': 20.0,
'write1_power': [11.718020000000001], 'read0_power': [4.40238],
'write0_power': [8.250219], 'read1_power': [4.126633],
'slew_hl': [0.8273725], 'slew_hl': [1.259555],
'min_period': 34, 'slew_lh': [0.9150649],
'delay_hl': [1.085861], 'write0_power': [4.988347],
'slew_lh': [0.5730144]} 'write1_power': [4.473887]}
else: else:
self.assertTrue(False) # other techs fail self.assertTrue(False) # other techs fail
# Check if no too many or too few results # Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys())) self.assertTrue(len(data.keys())==len(golden_data.keys()))
# Check each result
for k in data.keys(): self.assertTrue(self.check_golden_data(data,golden_data,0.25))
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)
globals.end_openram() globals.end_openram()

View File

@ -49,14 +49,9 @@ class timing_setup_test(openram_test):
# Check if no too many or too few results # Check if no too many or too few results
self.assertTrue(len(data.keys())==len(golden_data.keys())) 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) reload(characterizer)
globals.end_openram() globals.end_openram()

View File

@ -40,7 +40,7 @@ class lib_test(openram_test):
newname = filename.replace(".lib","_analytical.lib") newname = filename.replace(".lib","_analytical.lib")
libname = "{0}/{1}".format(OPTS.openram_temp,filename) libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) 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() globals.end_openram()

View File

@ -49,7 +49,7 @@ class lib_test(openram_test):
newname = filename.replace(".lib","_pruned.lib") newname = filename.replace(".lib","_pruned.lib")
libname = "{0}/{1}".format(OPTS.openram_temp,filename) libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) 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) reload(characterizer)
globals.end_openram() globals.end_openram()

View File

@ -48,7 +48,7 @@ class lib_test(openram_test):
for filename in lib_files: for filename in lib_files:
libname = "{0}/{1}".format(OPTS.openram_temp,filename) libname = "{0}/{1}".format(OPTS.openram_temp,filename)
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),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) reload(characterizer)
globals.end_openram() globals.end_openram()

View File

@ -64,10 +64,13 @@ class openram_test(openram_test):
self.assertTrue(len(files)>0) self.assertTrue(len(files)>0)
# grep any errors from the output # 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('ERROR',output)),0)
self.assertEqual(len(re.findall('WARNING',output)),0) self.assertEqual(len(re.findall('WARNING',output)),0)
# now clean up the directory # now clean up the directory
if os.path.exists(out_path): if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True) shutil.rmtree(out_path, ignore_errors=True)

View File

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

View File

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

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_freepdk45){
dont_use : true; dont_use : true;
map_only : true; map_only : true;
dont_touch : true; dont_touch : true;
area : 1032.3999375; area : 948.52275;
leakage_power () { leakage_power () {
when : "CSb"; when : "CSb";
value : 0.0008128352; value : 0.0021292;
} }
cell_leakage_power : 0; cell_leakage_power : 0;
bus(DATA){ bus(DIN){
bus_type : DATA; bus_type : DATA;
direction : inout; direction : in;
max_capacitance : 1.6728; max_capacitance : 1.6728;
min_capacitance : 0.052275; min_capacitance : 0.052275;
three_state : "!OEb & !clk";
memory_write(){ memory_write(){
address : ADDR; address : ADDR;
clocked_on : clk; clocked_on : clk;
} }
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 1.6728;
min_capacitance : 0.052275;
memory_read(){ memory_read(){
address : ADDR; address : ADDR;
} }
pin(DATA[1:0]){ pin(DOUT[1:0]){
timing(){ timing(){
timing_type : setup_rising; timing_type : setup_rising;
related_pin : "clk"; related_pin : "clk";
@ -130,26 +134,26 @@ cell (sram_2_16_1_freepdk45){
timing(){ timing(){
timing_sense : non_unate; timing_sense : non_unate;
related_pin : "clk"; related_pin : "clk";
timing_type : falling_edge; timing_type : rising_edge;
cell_rise(CELL_TABLE) { cell_rise(CELL_TABLE) {
values("0.054, 0.055, 0.061",\ values("0.227, 0.227, 0.231",\
"0.055, 0.056, 0.062",\ "0.227, 0.228, 0.232",\
"0.06, 0.061, 0.068"); "0.233, 0.234, 0.238");
} }
cell_fall(CELL_TABLE) { cell_fall(CELL_TABLE) {
values("0.066, 0.067, 0.075",\ values("2.555, 2.557, 2.569",\
"0.067, 0.068, 0.076",\ "2.556, 2.557, 2.569",\
"0.072, 0.073, 0.082"); "2.562, 2.563, 2.576");
} }
rise_transition(CELL_TABLE) { rise_transition(CELL_TABLE) {
values("0.013, 0.014, 0.026",\ values("0.02, 0.021, 0.028",\
"0.013, 0.015, 0.026",\ "0.02, 0.021, 0.028",\
"0.013, 0.015, 0.026"); "0.02, 0.021, 0.028");
} }
fall_transition(CELL_TABLE) { fall_transition(CELL_TABLE) {
values("0.023, 0.024, 0.037",\ values("0.11, 0.11, 0.114",\
"0.023, 0.024, 0.037",\ "0.109, 0.11, 0.113",\
"0.024, 0.024, 0.037"); "0.11, 0.11, 0.114");
} }
} }
} }
@ -298,19 +302,19 @@ cell (sram_2_16_1_freepdk45){
internal_power(){ internal_power(){
when : "!CSb & clk & !WEb"; when : "!CSb & clk & !WEb";
rise_power(scalar){ rise_power(scalar){
values("0.0159801855389"); values("0.025181683333333333");
} }
fall_power(scalar){ fall_power(scalar){
values("0.0159801855389"); values("0.025181683333333333");
} }
} }
internal_power(){ internal_power(){
when : "!CSb & !clk & WEb"; when : "!CSb & !clk & WEb";
rise_power(scalar){ rise_power(scalar){
values("0.0171325605389"); values("0.024945991666666667");
} }
fall_power(scalar){ fall_power(scalar){
values("0.0171325605389"); values("0.024945991666666667");
} }
} }
internal_power(){ internal_power(){
@ -326,20 +330,20 @@ cell (sram_2_16_1_freepdk45){
timing_type :"min_pulse_width"; timing_type :"min_pulse_width";
related_pin : clk; related_pin : clk;
rise_constraint(scalar) { rise_constraint(scalar) {
values("0.1125"); values("2.422");
} }
fall_constraint(scalar) { fall_constraint(scalar) {
values("0.1125"); values("2.422");
} }
} }
timing(){ timing(){
timing_type :"minimum_period"; timing_type :"minimum_period";
related_pin : clk; related_pin : clk;
rise_constraint(scalar) { rise_constraint(scalar) {
values("0.225"); values("4.844");
} }
fall_constraint(scalar) { 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; dont_use : true;
map_only : true; map_only : true;
dont_touch : true; dont_touch : true;
area : 134589.78; area : 142800.38999999998;
leakage_power () { leakage_power () {
when : "CSb"; when : "CSb";
value : 0.0004764706; value : 0.0252988;
} }
cell_leakage_power : 0; cell_leakage_power : 0;
bus(DATA){ bus(DIN){
bus_type : DATA; bus_type : DATA;
direction : inout; direction : in;
max_capacitance : 78.5936; max_capacitance : 78.5936;
min_capacitance : 2.45605; min_capacitance : 2.45605;
three_state : "!OEb & !clk";
memory_write(){ memory_write(){
address : ADDR; address : ADDR;
clocked_on : clk; clocked_on : clk;
} }
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){ memory_read(){
address : ADDR; address : ADDR;
} }
pin(DATA[1:0]){ pin(DOUT[1:0]){
timing(){ timing(){
timing_type : setup_rising; timing_type : setup_rising;
related_pin : "clk"; related_pin : "clk";
@ -130,26 +134,26 @@ cell (sram_2_16_1_scn3me_subm){
timing(){ timing(){
timing_sense : non_unate; timing_sense : non_unate;
related_pin : "clk"; related_pin : "clk";
timing_type : falling_edge; timing_type : rising_edge;
cell_rise(CELL_TABLE) { cell_rise(CELL_TABLE) {
values("0.474, 0.52, 0.888",\ values("0.945, 0.976, 1.139",\
"0.477, 0.522, 0.892",\ "0.948, 0.98, 1.143",\
"0.517, 0.561, 0.929"); "1.003, 1.036, 1.202");
} }
cell_fall(CELL_TABLE) { cell_fall(CELL_TABLE) {
values("0.582, 0.658, 1.26",\ values("11.211, 11.266, 11.754",\
"0.586, 0.661, 1.262",\ "11.212, 11.267, 11.755",\
"0.626, 0.7, 1.298"); "11.264, 11.319, 11.806");
} }
rise_transition(CELL_TABLE) { rise_transition(CELL_TABLE) {
values("0.155, 0.233, 1.087",\ values("0.605, 0.629, 0.98",\
"0.156, 0.235, 1.086",\ "0.605, 0.629, 0.979",\
"0.16, 0.239, 1.086"); "0.604, 0.628, 0.973");
} }
fall_transition(CELL_TABLE) { fall_transition(CELL_TABLE) {
values("0.277, 0.356, 1.502",\ values("11.17, 11.175, 1.284",\
"0.278, 0.358, 1.501",\ "11.167, 11.173, 1.284",\
"0.279, 0.363, 1.5"); "11.173, 11.179, 11.473");
} }
} }
} }
@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){ internal_power(){
when : "!CSb & clk & !WEb"; when : "!CSb & clk & !WEb";
rise_power(scalar){ rise_power(scalar){
values("4.92665"); values("2.1762222222222225");
} }
fall_power(scalar){ fall_power(scalar){
values("4.92665"); values("2.1762222222222225");
} }
} }
internal_power(){ internal_power(){
when : "!CSb & !clk & WEb"; when : "!CSb & !clk & WEb";
rise_power(scalar){ rise_power(scalar){
values("5.74515833333"); values("2.167955555555556");
} }
fall_power(scalar){ fall_power(scalar){
values("5.74515833333"); values("2.167955555555556");
} }
} }
internal_power(){ internal_power(){
@ -326,20 +330,20 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"min_pulse_width"; timing_type :"min_pulse_width";
related_pin : clk; related_pin : clk;
rise_constraint(scalar) { rise_constraint(scalar) {
values("1.875"); values("9.6875");
} }
fall_constraint(scalar) { fall_constraint(scalar) {
values("1.875"); values("9.6875");
} }
} }
timing(){ timing(){
timing_type :"minimum_period"; timing_type :"minimum_period";
related_pin : clk; related_pin : clk;
rise_constraint(scalar) { rise_constraint(scalar) {
values("3.75"); values("19.375");
} }
fall_constraint(scalar) { 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; dont_use : true;
map_only : true; map_only : true;
dont_touch : true; dont_touch : true;
area : 134589.78; area : 142800.38999999998;
leakage_power () { leakage_power () {
when : "CSb"; when : "CSb";
value : 0.000173; value : 0.000168;
} }
cell_leakage_power : 0; cell_leakage_power : 0;
bus(DATA){ bus(DIN){
bus_type : DATA; bus_type : DATA;
direction : inout; direction : in;
max_capacitance : 78.5936; max_capacitance : 78.5936;
min_capacitance : 2.45605; min_capacitance : 2.45605;
three_state : "!OEb & !clk";
memory_write(){ memory_write(){
address : ADDR; address : ADDR;
clocked_on : clk; clocked_on : clk;
} }
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){ memory_read(){
address : ADDR; address : ADDR;
} }
pin(DATA[1:0]){ pin(DOUT[1:0]){
timing(){ timing(){
timing_type : setup_rising; timing_type : setup_rising;
related_pin : "clk"; related_pin : "clk";
@ -130,16 +134,16 @@ cell (sram_2_16_1_scn3me_subm){
timing(){ timing(){
timing_sense : non_unate; timing_sense : non_unate;
related_pin : "clk"; related_pin : "clk";
timing_type : falling_edge; timing_type : rising_edge;
cell_rise(CELL_TABLE) { cell_rise(CELL_TABLE) {
values("0.556, 0.603, 1.044",\ values("0.54, 0.587, 1.028",\
"0.556, 0.603, 1.044",\ "0.54, 0.587, 1.028",\
"0.556, 0.603, 1.044"); "0.54, 0.587, 1.028");
} }
cell_fall(CELL_TABLE) { cell_fall(CELL_TABLE) {
values("0.556, 0.603, 1.044",\ values("0.54, 0.587, 1.028",\
"0.556, 0.603, 1.044",\ "0.54, 0.587, 1.028",\
"0.556, 0.603, 1.044"); "0.54, 0.587, 1.028");
} }
rise_transition(CELL_TABLE) { rise_transition(CELL_TABLE) {
values("0.024, 0.081, 0.61",\ values("0.024, 0.081, 0.61",\
@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){ internal_power(){
when : "!CSb & clk & !WEb"; when : "!CSb & clk & !WEb";
rise_power(scalar){ rise_power(scalar){
values("10.9314668117"); values("10.559086132533329");
} }
fall_power(scalar){ fall_power(scalar){
values("10.9314668117"); values("10.559086132533329");
} }
} }
internal_power(){ internal_power(){
when : "!CSb & !clk & WEb"; when : "!CSb & !clk & WEb";
rise_power(scalar){ rise_power(scalar){
values("10.9314668117"); values("10.559086132533329");
} }
fall_power(scalar){ fall_power(scalar){
values("10.9314668117"); values("10.559086132533329");
} }
} }
internal_power(){ internal_power(){
@ -336,10 +340,10 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"minimum_period"; timing_type :"minimum_period";
related_pin : clk; related_pin : clk;
rise_constraint(scalar) { rise_constraint(scalar) {
values("0.0"); values("0");
} }
fall_constraint(scalar) { fall_constraint(scalar) {
values("0.0"); values("0");
} }
} }
} }

View File

@ -78,27 +78,31 @@ cell (sram_2_16_1_scn3me_subm){
dont_use : true; dont_use : true;
map_only : true; map_only : true;
dont_touch : true; dont_touch : true;
area : 134589.78; area : 142800.38999999998;
leakage_power () { leakage_power () {
when : "CSb"; when : "CSb";
value : 0.0004764706; value : 0.0252988;
} }
cell_leakage_power : 0; cell_leakage_power : 0;
bus(DATA){ bus(DIN){
bus_type : DATA; bus_type : DATA;
direction : inout; direction : in;
max_capacitance : 78.5936; max_capacitance : 78.5936;
min_capacitance : 2.45605; min_capacitance : 2.45605;
three_state : "!OEb & !clk";
memory_write(){ memory_write(){
address : ADDR; address : ADDR;
clocked_on : clk; clocked_on : clk;
} }
bus(DOUT){
bus_type : DATA;
direction : out;
max_capacitance : 78.5936;
min_capacitance : 2.45605;
memory_read(){ memory_read(){
address : ADDR; address : ADDR;
} }
pin(DATA[1:0]){ pin(DOUT[1:0]){
timing(){ timing(){
timing_type : setup_rising; timing_type : setup_rising;
related_pin : "clk"; related_pin : "clk";
@ -130,26 +134,26 @@ cell (sram_2_16_1_scn3me_subm){
timing(){ timing(){
timing_sense : non_unate; timing_sense : non_unate;
related_pin : "clk"; related_pin : "clk";
timing_type : falling_edge; timing_type : rising_edge;
cell_rise(CELL_TABLE) { cell_rise(CELL_TABLE) {
values("0.458, 0.504, 0.871",\ values("0.928, 0.959, 1.113",\
"0.461, 0.506, 0.874",\ "0.931, 0.962, 1.116",\
"0.5, 0.544, 0.912"); "0.985, 1.018, 1.176");
} }
cell_fall(CELL_TABLE) { cell_fall(CELL_TABLE) {
values("0.573, 0.649, 1.251",\ values("11.214, 11.27, 11.756",\
"0.577, 0.652, 1.254",\ "11.22, 11.27, 11.761",\
"0.618, 0.69, 1.29"); "11.268, 11.323, 11.809");
} }
rise_transition(CELL_TABLE) { rise_transition(CELL_TABLE) {
values("0.153, 0.233, 1.085",\ values("0.599, 0.624, 0.968",\
"0.154, 0.234, 1.084",\ "0.599, 0.623, 0.97",\
"0.158, 0.237, 1.084"); "0.598, 0.623, 0.967");
} }
fall_transition(CELL_TABLE) { fall_transition(CELL_TABLE) {
values("0.276, 0.356, 1.5",\ values("11.157, 11.165, 11.446",\
"0.277, 0.357, 1.5",\ "11.159, 11.162, 1.271",\
"0.278, 0.363, 1.5"); "11.158, 11.165, 11.47");
} }
} }
} }
@ -298,19 +302,19 @@ cell (sram_2_16_1_scn3me_subm){
internal_power(){ internal_power(){
when : "!CSb & clk & !WEb"; when : "!CSb & clk & !WEb";
rise_power(scalar){ rise_power(scalar){
values("4.42361814306"); values("2.033783461111111");
} }
fall_power(scalar){ fall_power(scalar){
values("4.42361814306"); values("2.033783461111111");
} }
} }
internal_power(){ internal_power(){
when : "!CSb & !clk & WEb"; when : "!CSb & !clk & WEb";
rise_power(scalar){ rise_power(scalar){
values("4.97118480973"); values("2.032705683333334");
} }
fall_power(scalar){ fall_power(scalar){
values("4.97118480973"); values("2.032705683333334");
} }
} }
internal_power(){ internal_power(){
@ -326,20 +330,20 @@ cell (sram_2_16_1_scn3me_subm){
timing_type :"min_pulse_width"; timing_type :"min_pulse_width";
related_pin : clk; related_pin : clk;
rise_constraint(scalar) { rise_constraint(scalar) {
values("1.875"); values("9.6875");
} }
fall_constraint(scalar) { fall_constraint(scalar) {
values("1.875"); values("9.6875");
} }
} }
timing(){ timing(){
timing_type :"minimum_period"; timing_type :"minimum_period";
related_pin : clk; related_pin : clk;
rise_constraint(scalar) { rise_constraint(scalar) {
values("3.75"); values("19.375");
} }
fall_constraint(scalar) { 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. # Reset the static duplicate name checker for unit tests.
import hierarchy_design import hierarchy_design
hierarchy_design.hierarchy_design.name_map=[] 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. """ """ This is used to compare relative values. """
import debug import debug
relative_diff = abs(value1 - value2) / max(value1,value2) relative_diff = self.relative_diff(value,actual_value)
check = relative_diff <= error_tolerance check = relative_diff <= error_tolerance
if not check: if check:
self.fail("NOT CLOSE {0} {1} relative diff={2}".format(value1,value2,relative_diff)) debug.info(2,"CLOSE\t{0: <10}\t{1:.3f}\t{2:.3f}\tdiff={3:.1f}%".format(key,value,actual_value,relative_diff*100))
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!
return True 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. """Compare two files.
Arguments: Arguments:
f1 -- First file name filename1 -- First file name
f2 -- Second file name filename2 -- Second file name
Return value: Return value:
@ -106,61 +151,93 @@ class openram_test(unittest.TestCase):
(?: [Ee] [+-]? \d+ ) ? (?: [Ee] [+-]? \d+ ) ?
""" """
rx = re.compile(numeric_const_pattern, re.VERBOSE) rx = re.compile(numeric_const_pattern, re.VERBOSE)
with open(f1, 'rb') as fp1, open(f2, 'rb') as fp2: fp1 = open(filename1, 'rb')
while True: fp2 = open(filename2, 'rb')
b1 = fp1.readline().decode('utf-8') mismatches=0
b2 = fp2.readline().decode('utf-8') line_num=0
#print "b1:",b1, while True:
#print "b2:",b2, 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 # 2. Remove the floats from the string
if b1 != b2: for f in line1_floats:
self.fail("MISMATCH Line: {0}\n!=\nLine: {1}".format(b1,b2)) 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 # 3. Convert to floats rather than strings
if len(b1_floats)!=len(b2_floats): line1_floats = [float(x) for x in line1_floats]
self.fail("MISMATCH Length {0} != {1}".format(len(b1_floats),len(b2_floats))) line2_floats = [float(x) for x in line1_floats]
for (f1,f2) in zip(b1_floats,b2_floats):
if not self.relative_compare(float(f1),float(f2),error_tolerance): # 4. Check if remaining string matches
self.fail("MISMATCH Float {0} != {1}".format(f1,f2)) 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: # 5. Now compare that the floats match
return 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,filename1,filename2):
def isdiff(self,file1,file2):
""" This is used to compare two files and display the diff if they are different.. """ """ This is used to compare two files and display the diff if they are different.. """
import debug import debug
import filecmp import filecmp
import difflib import difflib
check = filecmp.cmp(file1,file2) check = filecmp.cmp(filename1,filename2)
if not check: if not check:
debug.info(2,"MISMATCH {0} {1}".format(file1,file2)) debug.error("MISMATCH file1={0} file2={1}".format(filename1,filename2))
f1 = open(file1,"r") f1 = open(filename1,"r")
s1 = f1.readlines() s1 = f1.readlines().decode('utf-8')
f2 = open(file2,"r") f1.close()
f2 = open(filename2,"r").decode('utf-8')
s2 = f2.readlines() s2 = f2.readlines()
f2.close()
mismatches=0
for line in difflib.unified_diff(s1, s2): for line in difflib.unified_diff(s1, s2):
debug.info(3,line) mismatches += 1
self.fail("MISMATCH {0} {1}".format(file1,file2)) self.error("DIFF LINES:",line)
if mismatches>10:
return False
return False
else: 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): def header(filename, technology):
# Skip the header for gitlab regression # Skip the header for gitlab regression

View File

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

View File

@ -1,10 +1,10 @@
*********************** "cell_6t" ****************************** *********************** "cell_6t" ******************************
.SUBCKT cell_6t bl br wl vdd gnd .SUBCKT cell_6t bl br wl vdd gnd
M_1 net_1 net_2 vdd vdd p W='0.9u' L=1.2u M_1 Q Qb 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_2 Qb Q vdd vdd p W='0.9u' L=1.2u
M_3 br wl net_2 gnd n W='1.2u' L=0.6u M_3 br wl Qb gnd n W='1.2u' L=0.6u
M_4 bl wl net_1 gnd n W='1.2u' L=0.6u M_4 bl wl Q gnd n W='1.2u' L=0.6u
M_5 net_2 net_1 gnd gnd n W='2.4u' L=0.6u M_5 Qb Q 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_6 Q Qb gnd gnd n W='2.4u' L=0.6u
.ENDS $ cell_6t .ENDS $ cell_6t

View File

@ -2,28 +2,28 @@
.SUBCKT write_driver din bl br en vdd gnd .SUBCKT write_driver din bl br en vdd gnd
**** Inverter to conver Data_in to data_in_bar ****** **** 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_1 din_bar 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_2 din_bar din vdd vdd p W='2.1*1u' L=0.6u
**** 2input nand gate follwed by inverter to drive BL ****** **** 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_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_5 din_bar_gated 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_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_7 net_1 din_bar_gated 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_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****** **** 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_9 din_gated 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_10 din_gated 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_11 net_8 din_bar 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_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_13 net_6 din_gated 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_14 net_6 din_gated gnd gnd n W='1.2*1u' L=0.6u
************************************************ ************************************************