Skip to content
Snippets Groups Projects
Commit 37828543 authored by Alexandros Asonitis's avatar Alexandros Asonitis
Browse files

Memristor Version 4.0 released!

parent 1bfffda0
No related branches found
No related tags found
No related merge requests found
...@@ -101,6 +101,15 @@ class HP4155a(object): ...@@ -101,6 +101,15 @@ class HP4155a(object):
command = f":PAGE:MEAS:VARD:OFFS {vard['offset']};RAT {vard['ratio']};COMP {vard['comp']};PCOM {vard['pcomp']}" command = f":PAGE:MEAS:VARD:OFFS {vard['offset']};RAT {vard['ratio']};COMP {vard['comp']};PCOM {vard['pcomp']}"
self.inst.write(command) self.inst.write(command)
def setup_pulse(self,pulse:dict):
command = f":PAGE:MEAS:PULS:BASE {pulse['base']};PER {pulse['period']};WIDTH {pulse['width']}"
self.inst.write(command)
#for pulse
def range_mode(self,smu_number,range):
command = f":PAGE:MEAS:MSET:SMU{smu_number}:RANG:MODE {range}"
self.inst.write(command)
#----------------------------------------------------------------------sampling measure functions part2 first goal---------------------------- #----------------------------------------------------------------------sampling measure functions part2 first goal----------------------------
def setup_sampling(self,parameters:dict): def setup_sampling(self,parameters:dict):
command = f"PAGE:MEAS:SAMP:MODE {parameters['mode']};HTIM {parameters['hold']};IINT {parameters['interval']};POIN {parameters['points']};FILT {parameters['filter']}" command = f"PAGE:MEAS:SAMP:MODE {parameters['mode']};HTIM {parameters['hold']};IINT {parameters['interval']};POIN {parameters['points']};FILT {parameters['filter']}"
...@@ -251,6 +260,11 @@ class HP4155a(object): ...@@ -251,6 +260,11 @@ class HP4155a(object):
parameters = dict.fromkeys(keys) parameters = dict.fromkeys(keys)
return parameters return parameters
def pulse_dict(self):
keys = ('base','width','period')
parameters = dict.fromkeys(keys)
return parameters
\ No newline at end of file
"""
This is a python file containing all the important functions for memristor measurement
Available Functions
measurements in the HP4155a
plot results
create data frame
ini file decoder
enabing and disabling widgets for jupyter(lists)
"""
import sys
sys.path.insert(0, '..') #append parent directory
import hp4155a
import matplotlib.pyplot as plt
import tkinter as tk
from tkinter import filedialog
import tkinter.messagebox
import numpy as np
from IPython.display import display, clear_output
import pandas as pd
from datetime import datetime
import ipywidgets as widgets
import time
import os
#contact check between two SMUs (i,j)
def contact_check(i,j,device):
smu = [1,2,3,4]
device.measurement_mode('SAMP')
parameters ={
'mode' : 'LIN',
'hold': 0,
'interval':2e-3,
'points': 1,
'filter': 'OFF',
'value':0.01, #voltage value
'comp':0.1 #compliance value
}
device.setup_sampling(parameters)
device.auto_sampling_time('ON')
device.integration_time('MED')
smu_v = device.smu_dict()
smu_v.update(
vname = f'V{i}',
iname = f'I{i}',
mode = 'V',
func = 'CONS'
)
device.setup_smu(i,smu_v)
smu_ground = device.smu_dict()
smu_ground.update(
vname =f'V{j}',
iname = f'I{j}',
mode = 'COMM',
func='CONS'
)
device.setup_smu(j,smu_ground)
#one smu is measuring
#one smu is ground
#set voltage and compliance
device.setup_smu_sampling(i,parameters)
#smus to remove
smu_disable = smu.copy()
smu_disable.remove(i)
smu_disable.remove(j)
for number in smu_disable:
device.smu_disable(number)
device.user_function(f'R{i}{j}','OHM',f'V{i}/I{i}')
device.display_variable('X','@TIME')
device.display_variable('Y1',f'R{i}{j}')
device.single_measurement()
while device.operation_completed() == False:
time.sleep(2)
R = device.return_values(f'R{i}{j}')[0] #only the first value
print(f"R{i}{j}:{'{:.2e}'.format(R)} Ohm")
device.del_user_functions()
device.autoscaling()
return R
#these are all the sampling checks
def regular_contact_check(device):
resistances = {}
for i in range(1,4): # iterate through smus 1-4
for j in range(4,i,-1):
"""
We have the following pairs in order
1-4,1-3,1-2,2-4,2-3,3-4
"""
R=contact_check(i,j,device)
resistances[f"{i}-{j}"] = R
#convert dictionary to df
df = pd.DataFrame(resistances.items(), columns=['SMU pair', 'Resistance (Ohm)'])
return df
def EBL(device):
# EBL are SMUs 1-4 and 2-3
resistances = {}
for i,j in zip(range(1,3),range(4,2,-1)): #loop simultaneously 1-4,2-3 pairs
R = contact_check(i,j,device)
resistances[f"{i}-{j}"] = R
#convert dictionary to df
df = pd.DataFrame(resistances.items(), columns=['SMU pair', 'Resistance (Ohm)'])
return df
def OL(device):
# OL smu 3-4,1-2
resistances= {}
for i,j in zip(range(3,0,-2),range(4,1,-2)): #loop simultaneously 3-4 , 1-2 pairs
R = contact_check(i,j,device)
resistances[f"{i}-{j}"] = R
#convert dictionary to df
df = pd.DataFrame(resistances.items(), columns=['SMU pair', 'Resistance (Ohm)'])
return df
#double sweep from start to stop and then from start to stop
def sweep(start,stop,step,comp,integration,device): #step cannot be negative
if start < stop and step < 0 :
step = -step
elif start > stop and step > 0 :
step = -step
smu_v = device.smu_dict()
smu_ground = device.smu_dict()
parameters = device.var1_dict()
smu_v.update(
iname = 'I2',
vname = 'V2',
mode = 'V',
func = 'VAR1'
)
smu_ground.update(
iname ='I4',
vname = 'V4',
mode = 'COMM',
func = 'CONS'
)
parameters.update(
mode ='DOUB',
start = start,
stop = stop,
step = step,
comp = comp,
pcomp = 0
)
#disable smus 1 and 3
device.measurement_mode('SWE')
device.smu_disable(1)
device.smu_disable(3)
device.setup_smu(2,smu_v)
device.setup_smu(4,smu_ground)
device.setup_var1(parameters)
device.integration_time(integration)
#display variables
device.display_variable('X','V2')
device.display_variable('Y1','I2')
#execute measurement
device.single_measurement()
while device.operation_completed()==False:
time.sleep(2)
device.autoscaling()
#return values
V=device.return_values('V2')
I=device.return_values('I2')
#convert the list to np.array to return the absolute values for the logarithmic scale
V = np.array(V)
I = np.array(I)
#return all values to the function
return V, I
#sampling check
def sampling_check(voltage,device):
parameters ={
'mode' : 'LIN',
'hold': 0,
'interval':2e-3,
'points': 5,
'filter': 'OFF',
'value':voltage, #voltage value
'comp':0.1 #compliance value
}
smu_v = device.smu_dict()
smu_ground = device.smu_dict()
smu_v.update(
iname = 'I2',
vname = 'V2',
mode = 'V',
func = 'CONS'
)
smu_ground.update(
iname ='I4',
vname = 'V4',
mode = 'COMM',
func = 'CONS'
)
device.measurement_mode('SAMP')
device.smu_disable(1)
device.smu_disable(3)
device.setup_smu(2,smu_v)
device.setup_smu(4,smu_ground)
device.setup_smu_sampling(2,parameters)
device.setup_sampling(parameters)
device.integration_time('LONG')
#remove total sampling time
device.auto_sampling_time('ON')
device.user_function('R','OHM','V2/I2')
device.display_variable('X','@INDEX')
device.display_variable('Y1','R')
device.single_measurement()
while device.operation_completed() == False:
time.sleep(2)
index = np.array(device.return_values('@INDEX'))
R = np.array(device.return_values('R'))
R_mean = np.average(R)
device.del_user_functions()
device.autoscaling()
# Plot the results
fig,ax = plt.subplots()
ax.set_title(f"Average Resistance(Sampling Check):{'{:.2e}'.format(R_mean)} Ohm")
ax.set_yscale('log')
ax.set_ylabel('Resistance (Ohm)')
ax.set_xlabel('Sampling Index')
ax.set_xticks(index)
ax.scatter(index,np.absolute(R),label = f"Voltage={voltage}V")
ax.legend()
display(fig)
return R_mean
#new (retention)
def retention(voltage,period,duration,device):
parameters ={
'mode' : 'LIN',
'hold': 0,
'interval':2e-3,
'points': 0,
'filter': 'OFF',
'value':voltage, #voltage value
'comp':0.1 #compliance value
}
smu_v = device.smu_dict()
smu_ground = device.smu_dict()
smu_v.update(
iname = 'I2',
vname = 'V2',
mode = 'V',
func = 'CONS'
)
smu_ground.update(
iname ='I4',
vname = 'V4',
mode = 'COMM',
func = 'CONS'
)
device.measurement_mode('SAMP')
device.smu_disable(1)
device.smu_disable(3)
device.setup_smu(2,smu_v)
device.setup_smu(4,smu_ground)
device.setup_smu_sampling(2,parameters)
device.integration_time('LONG')
device.total_sampling_time(duration)
if int(duration/period)+1<=10001:
parameters.update(points=int(duration/period)+1)
else:
parameters.update(points = 'MAX')
device.setup_sampling(parameters)
device.integration_time('MED')
device.user_function('R','OHM','V2/I2')
device.display_variable('X','@TIME')
device.display_variable('Y1','R')
device.single_measurement()
while device.operation_completed() == False:
time.sleep(2)
TIME = device.return_values('@TIME')
R = device.return_values('R')
TIME = np.array(TIME)
R = np.array(R)
device.del_user_functions()
device.autoscaling()
return TIME,R
#plot sweep results
def plot_sweep(x,y,title):
#plot results
fig, (ax1, ax2) = plt.subplots(2,sharex=True,figsize=(8,6)) #the plots share the same x axis
fig.suptitle(title)
ax1.set_title('Linear I')
ax1.set(xlabel='Voltage(V)',ylabel='Current(A)')
ax2.set_title('Logarithmic I')
ax2.set(xlabel='Voltage(V)',ylabel='Current(A)')
ax2.set_yscale('log')
ax1.plot(x,y)
ax2.plot(x,np.absolute(y))
fig.tight_layout()
display(fig)
def plot_retention(x,y):
fig, ax = plt.subplots()
fig.suptitle('Retention')
ax.set(xlabel='time(s)',ylabel='Resistance(Ohm)')
ax.set_yscale('log')
ax.set_xscale('linear')
ax.plot(x,y)
display(fig)
def create_data_frame(x,y):
header = ['V(V)','ABSV(V)',"I(A)",'ABSI(A)',"R(Ohm)"]
data = {header[0]:x,header[1]:np.absolute(x),header[2]:y,header[3]:np.absolute(y),header[4]:np.divide(x,y)}
df = pd.DataFrame(data)
return df
def create_retention_data_frame(x,y):
header = ['Time(s)','R(Ohm)']
data = {header[0]:x,header[1]:y}
df = pd.DataFrame(data)
return df
#write results to file
def write_to_file(file,title:list,df):
#append escape character after each element
index = 1
while index <= len(title):
title.insert(index,"\n")
index = index+2
#write to file
with open(file,'a') as f:
f.writelines(title)
f.write("\n")
f.write(df.to_string())
f.write("\n\n")
#### new functions ##############
def disable_widgets(widgets_list):
for widget in widgets_list:
widget.disabled = True
def change_state(widgets_list):
for widget in widgets_list:
widget.disabled = not widget.disabled
def enable_widgets(widgets_list):
for widget in widgets_list:
widget.disabled = False
#a check values function
def check_values(step,set_voltage,reset_voltage):
valid = True
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
if step > abs(set_voltage) or step > abs(reset_voltage) or step==0:#invalid parameter setting
valid = False
tkinter.messagebox.showerror(message="Invalid parameter setting!")
#now if the set-reset voltages have the same polarity show a warning
elif set_voltage*reset_voltage>0:
valid = tk.messagebox.askokcancel(message="Set-Reset voltages have the same polarity. Continue?")
else:
pass
root.destroy()
return valid
def information_box(information):
#open dialog and hide the main window
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
#display meaagebox
tkinter.messagebox.showinfo(message=information)
root.destroy()
#choose directory to save measurement results
#and check if you have access
def check_writable(folder):
filename = "test.txt"
file = os.path.join(folder,filename)
#protection against removing existing file in python
i=1
while os.path.exists(file):
filename=f"test{i}.txt"
file = os.path.join(folder,filename)
try:
with open(file,'a'):
writable = True
os.remove(file)
except:
writable = False
information_box(f"{folder} is not writable!")
return writable
def choose_folder():
root = tk.Tk()
root.withdraw()
root.lift() #show window above all other applications
root.attributes("-topmost", True)#window stays above all other applications
#choose nonemty folder
folder = tk.filedialog.askdirectory()
while folder == '':
folder = tk.filedialog.askdirectory()
#check if writable in a while loop
writable=check_writable(folder)
while writable == False:
#choose a correct folder
folder = tk.filedialog.askdirectory()
while folder == '':
folder = tk.filedialog.askdirectory()
#check writable if not repeat
writable=check_writable(folder)
root.destroy()
return folder
def upload_results(source_file,target_file,target_file_dir):
"""
New function (UPLOAD RESULTS)
IMPORTANT FOR ALL MEASUREMENTS
THE RESULTS ARE MOVED FROM SOURCE FILE TO TARGET FILE EVEN LOCALLY
"""
while True:
try:
with (open(source_file,'r') as source,open(target_file,'a') as target):
target.write(source.read())
os.remove(source_file)
return source_file,target_file,target_file_dir
except:
information_box(f"{target_file} is no longer accessible. Please change directory")
target_file_dir = choose_folder()
filename = os.path.basename(target_file)
target_file =os.path.join(target_file_dir,filename)
#and then try again
\ No newline at end of file
import sys
sys.path.insert(0, '..') #append parent directory
import ipywidgets as widgets
import tkinter as tk
from tkinter import filedialog
import tkinter.messagebox
import os
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
import module
import time
import pandas as pd
from IPython.display import display, clear_output
#widgets interactivity
def add_widgets_to_list(source_dictionary,target_list):
for widget in source_dictionary.values():
target_list.append(widget)
def check_pulse(dictionary):
#check if number of pulses is ok
if dictionary['pulses'].value < 0:
dictionary['pulses'].value = -dictionary['pulses'].value
elif dictionary['pulses'].value==0:
dictionary['pulses'].value = 1
else:
pass
# Restriction: pulse period ≥ pulse width + 4 ms
if dictionary['period'].value < dictionary['width'].value+4e-3:
dictionary['period'].value = dictionary['width'].value+4e-3
#sweep pulse measurement
def sweep_meas(dictionary,device):
smu_v = device.smu_dict()
smu_ground = device.smu_dict()
parameters = device.var1_dict()
smu_v.update(
iname = 'I2',
vname = 'V2',
mode = 'VPULSE',
func = 'VAR1'
)
smu_ground.update(
iname ='I4',
vname = 'V4',
mode = 'COMM',
func = 'CONS'
)
parameters.update(
mode ='SING',
start = dictionary['start'].value,
stop = dictionary['stop'].value,
step = (dictionary["stop"].value-dictionary["start"].value)/(dictionary["pulses"].value-1), #define the number of steps given specific pulses
comp = dictionary['comp'].value,
pcomp = 0,
base = dictionary["base"].value,
width = dictionary["width"].value,
period= dictionary["period"].value
)
device.smu_disable(1)
device.smu_disable(3)
device.measurement_mode("SWE")
device.setup_smu(2,smu_v)
device.setup_smu(4,smu_ground)
device.setup_var1(parameters)
device.display_variable("X","V2")
device.display_variable("Y1",'I2')
device.range_mode(4,"AUTO")
device.range_mode(2,"AUTO")
device.setup_pulse(parameters)
device.integration_time(dictionary["integration"].value)
t0 = time.time()
device.single_measurement()
while device.operation_completed()== False:
pass
t1 = time.time()
# get the execution time
elapsed_time = t1 - t0
device.autoscaling()
I_i=device.return_values("I2")
V_i=device.return_values("V2")
R_i = np.divide(V_i,I_i)
expected_time = dictionary["period"].value*dictionary["pulses"].value
times = (elapsed_time,expected_time)
values = (V_i,I_i,R_i)
del device
return times,values
def plot_sweep_pulse(values):
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel("V(V)")
ax1.set_ylabel("I(A)",color=color)
ax1.set_yscale('log')
ax1.plot(values[0],np.abs(values[1]),color=color)
ax1.tick_params(axis ='y', labelcolor = color,which = 'both')
# Adding Twin Axes
ax2 = ax1.twinx()
color = 'tab:green'
ax2.set_ylabel("R(Ohm)",color = color)
ax2.plot(values[0],np.abs(values[2]),color = color)
ax2.tick_params(axis ='y', labelcolor = color,which = 'both')
ax2.set_yscale('log')
fig.suptitle("Sweep Pulse Measurement Results")
display(fig)
def save_sweep(folder,sample_dict,values,times,sweep_dict):
filename = f"{sample_dict['series'].value}_{sample_dict['field'].value}_{sample_dict['dut'].value}.txt"
file = os.path.join(folder,filename)
with open(file,"a") as f:
date = str(datetime.today().replace(microsecond=0))
f.write(f"Sweep Pulse Measurement at {date}"+"\n")
f.write(f"period(s):{sweep_dict['period'].value}"+"\n")
f.write(f"width(s):{sweep_dict['width'].value}"+"\n")
f.write(f"base value(V):{sweep_dict['base'].value}"+"\n")
f.write(f"execution time(s):{times[0]}"+"\n")
f.write(f"expected time(s):{times[1]}"+"\n")
f.write(f"number of pulses:{sweep_dict['pulses'].value}"+"\n")
f.write(f"current compliance(A):{sweep_dict['comp'].value}"+"\n")
f.write(f"voltage:{sweep_dict['start'].value}V to {sweep_dict['stop'].value}V"+"\n")
f.write(f"integration time:{sweep_dict['integration'].value}"+"\n\n")
zipped = list(zip(values[0],values[1], values[2]))
df = pd.DataFrame(zipped, columns=['VPULSE(V)', 'IPULSE(A)', 'RPULSE(Ohm)'])
f.write("Results Sweep Pulse:\n")
f.write(df.to_string())
f.write("\n\n\n")
def constant_meas(dictionary,device):
smu_v = device.smu_dict()
smu_ground = device.smu_dict()
sweep_params = device.var1_dict()
smu_help = device.smu_dict() #this is the uncontacted smu
smu_v.update(
iname = 'I2',
vname = 'V2',
mode = 'V',
func = 'CONS'
)
smu_help.update(
iname = 'I3',
vname = 'V3',
mode = 'VPULSE',
func = 'VAR1'
)
smu_ground.update(
iname ='I4',
vname = 'V4',
mode = 'COMM',
func = 'CONS'
)
sweep_params.update(
mode ='SING',
start = 0,
stop = 10,
step = 10/(dictionary["pulses"].value-1), #define the number of steps given specific pulses
comp = 0.1,
pcomp = 0,
base = dictionary["base"].value,
width = dictionary["width"].value,
period= dictionary["period"].value
)
#the constant smu
cons = {
'value':dictionary["voltage"].value,
'comp':dictionary["comp"].value
}
device.measurement_mode("SWE")
device.smu_disable(1)
device.setup_smu(2,smu_v)
device.setup_smu(3,smu_help)
device.setup_smu(4,smu_ground)
device.setup_var1(sweep_params)
device.setup_pulse(sweep_params)
device.setup_cons_smu(2,cons)
device.user_function('R','OHM','V2/I2')
device.display_variable("X","@INDEX")
device.display_variable("Y1",'R')
device.range_mode(4,"AUTO")
device.range_mode(2,"AUTO")
device.range_mode(3,"AUTO")
device.integration_time(dictionary["integration"].value)
device.variables_to_save(['@INDEX','V2','I2','R'])
t0 = time.time()
device.single_measurement()
while device.operation_completed()== False:
pass
t1 = time.time()
# get the execution time
elapsed_time = t1 - t0
I_i=device.return_values("I2")
V_i=device.return_values("V2")
R_i = device.return_values('R')
expected_time = dictionary["period"].value*dictionary["pulses"].value
times = (elapsed_time,expected_time)
values = (V_i,I_i,R_i)
device.del_user_functions()
device.autoscaling()
return times,values
def plot_constant_pulse(values):
index =[]
for i in range(len(values[0])):
index.append(i+1)
fig, ax1 = plt.subplots()
color = 'tab:red'
ax1.set_xlabel("Index(Pulse number)")
ax1.set_ylabel("I(A)",color=color)
ax1.set_yscale('log')
ax1.plot(index,np.abs(values[1]),color=color,label = "Voltage(V):"+str(min(values[0])))
ax1.tick_params(axis ='y', labelcolor = color,which = 'both')
# Adding Twin Axes
ax2 = ax1.twinx()
color = 'tab:green'
ax2.set_ylabel("R(Ohm)",color = color)
ax2.plot(index,np.abs(values[2]),color=color)
ax2.set_yscale('log')
ax2.tick_params(axis ='y', labelcolor = color,which = 'both')
fig.suptitle("Constant Pulse Measurement Results")
ax1.set_title("Voltage:"+str(min(values[0]))+"V")
display(fig)
def save_constant(folder,sample_dict,values,times,cons_dict):
filename = f"{sample_dict['series'].value}_{sample_dict['field'].value}_{sample_dict['dut'].value}.txt"
file = os.path.join(folder,filename)
with open(file,"a") as f:
date = str(datetime.today().replace(microsecond=0))
f.write(f"Constant Pulse Measurement at {date}"+"\n")
f.write(f"period(s):{cons_dict['period'].value}"+"\n")
f.write(f"width(s):{cons_dict['width'].value}"+"\n")
f.write(f"base value(V):{cons_dict['base'].value}"+"\n")
f.write(f"execution time(s):{times[0]}"+"\n")
f.write(f"expected time(s):{times[1]}"+"\n")
f.write(f"number of pulses:{cons_dict['pulses'].value}"+"\n")
f.write(f"current compliance(A):{cons_dict['comp'].value}"+"\n")
f.write(f"constant voltage:{cons_dict['voltage'].value}V"+"\n")
f.write(f"integration time:{cons_dict['integration'].value}"+"\n\n")
zipped = list(zip(values[0],values[1], values[2]))
df = pd.DataFrame(zipped, columns=['VPULSE(V)', 'IPULSE(A)', 'RPULSE(Ohm)'])
f.write("Results Constant Pulse:\n")
f.write(df.to_string())
f.write("\n\n\n")
import ipywidgets as widgets
#sample interface
style = {'description_width': 'initial'}
def constant_pulse():
voltage = widgets.BoundedFloatText(
value = 10,
min = -100,
max = 100,
step = 1,
description = 'Constant Voltage(V):',
style=style,
)
comp = widgets.BoundedFloatText(
value = 0.1,
min = -0.1,
max = 0.1,
step = 0.01,
description = 'Compliance(A):',
style=style,
)
pulses = widgets.IntText(
value = 100,
description = 'Number of Pulses:',
style=style,
)
period = widgets.BoundedFloatText(
value = 5e-3,
min = 5e-3,
max = 1,
step = 5e-3,
description ='Pulse Period(s):',
style=style,
)
width = widgets.BoundedFloatText(
value = 5e-4,
min = 5e-4,
max = 1e-1,
step= 5e-4,
description ='Pulse Width(s):',
style=style,
)
base = widgets.BoundedFloatText(
value = 0,
min = -100,
max = 100,
step = 1,
description = 'Base Voltage(V):',
style=style
)
integration =widgets.Dropdown(
options=['SHORt', 'MEDium', 'LONG'],
value='MEDium',
description='Integration:',
style=style
)
pulse_parameters = widgets.VBox([pulses,period,width,base])
smu_parameters = widgets.VBox([voltage,comp,integration])
constant_pulse_widgets = widgets.HBox([smu_parameters,pulse_parameters])
constant_pulse_dict = {
'voltage': voltage,
'comp':comp,
'pulses':pulses,
'period':period,
'width':width,
'base':base,
'integration':integration
}
return constant_pulse_widgets,constant_pulse_dict
def sweep_pulse():
start_voltage = widgets.BoundedFloatText(
value = 0,
min = -100,
max = 100,
step = 1,
description = 'Start Voltage(V):',
style=style,
)
stop_voltage = widgets.BoundedFloatText(
value = 15,
min = -100,
max = 100,
step = 1,
description = 'Stop Voltage(V):',
style=style,
)
comp = widgets.BoundedFloatText(
value = 0.1,
min = -0.1,
max = 0.1,
step = 0.01,
description = 'Compliance(A):',
style=style,
)
pulses = widgets.IntText(
value = 100,
description = 'Number of Pulses:',
style=style,
)
period = widgets.BoundedFloatText(
value = 5e-3,
min = 5e-3,
max = 1,
step = 5e-3,
description ='Pulse Period(s):',
style=style,
)
width = widgets.BoundedFloatText(
value = 5e-4,
min = 5e-4,
max = 1e-1,
step= 5e-4,
description ='Pulse Width(s):',
style=style,
)
base = widgets.BoundedFloatText(
value = 0,
min = -100,
max = 100,
step = 1,
description = 'Base Voltage(V):',
style=style
)
integration =widgets.Dropdown(
options=['SHORt', 'MEDium', 'LONG'],
value='MEDium',
description='Integration:',
style=style
)
pulse_parameters = widgets.VBox([pulses,period,width,base])
smu_parameters = widgets.VBox([start_voltage,stop_voltage,comp,integration])
sweep_pulse_widgets = widgets.HBox([smu_parameters,pulse_parameters])
sweep_pulse_dict = {
'start': start_voltage,
'stop':stop_voltage,
'comp':comp,
'pulses':pulses,
'period':period,
'width':width,
'base':base,
'integration':integration
}
return sweep_pulse_widgets,sweep_pulse_dict
### this is the new memrstor measurement (set and reset as many times as the user wants and full sweeps with a button)
from help import *
import ipywidgets as widgets
from keyboard import add_hotkey,remove_hotkey
# pulsed libraries
from help_pulse import *
#create temporary file to store the results localy
temp_file= os.path.join(os.getcwd(),'tempfile.txt')
# the three naming fields
sample_series= widgets.Text(
value= '',
placeholder ='Enter text here:',
description = 'sample series:',
style = {'description_width': 'initial'}
)
field = widgets.Text(
value= '',
placeholder ='Enter text here:',
description = 'Field:',
style = {'description_width': 'initial'},
)
DUT = widgets.Text(
value= '',
placeholder ='Enter text here:',
description = 'DUT:',
style = {'description_width': 'initial'},
)
#choose a new folder button
new_folder = widgets.Button(description='change folder')
image = widgets.Image(
value=open("schematic.png", "rb").read(),
format='png',
width=300,
height=100,
)
contact_check = widgets.Button(description = 'CONTACT CHECK')
qcc = widgets.Button(description = 'QUICK CONTACT CHECK',layout=widgets.Layout(width='80%'),style={"button_width": "auto"})
qcc_select = widgets.RadioButtons(description = 'QCC type:',options = ['EBL','OL'])
vertical1 = widgets.VBox([sample_series,field,DUT,new_folder,contact_check,qcc,qcc_select])
vertical2 = widgets.VBox([image])
all_text_boxes = widgets.HBox([vertical1,vertical2])
#first series of parameters
step = widgets.BoundedFloatText(
value=0.01,
min=0,
max=100,
step=0.01,
description='Step(V):',
)
integration_time=widgets.Dropdown(
options=['SHORt', 'MEDium', 'LONG'],
value='MEDium',
description='Integration:',
#style = {'description_width': 'initial'},
)
sampling=widgets.Checkbox(description='sampling check')
auto_qcc = widgets.Checkbox(
description = 'Auto QCC after Reset',
style = {'description_width': 'initial'},
value = True
)
# THE BUTTONS
#create buttons as it shown in the how_buttons_look
set=widgets.Button(description='SET')
reset=widgets.Button(description='RESET')
full=widgets.Button(description='FULL SWEEP')
number = widgets.BoundedIntText(value=1,min=1,max=sys.maxsize,step=1,description='full sweeps:',disabled=False) #number of measuremts for the full sweep
retention_button=widgets.Button(description='RETENTION')
#parameter boxes
Vset=widgets.BoundedFloatText(
value=1,
min=-100,
max=100,
step=0.1,
description='Voltage(V):',
)
#parameter buttons
CC_vset=widgets.BoundedFloatText(
value=1e-3,
min=-0.1,
max=0.1,
step=0.01,
description= 'Comp(A):',
)
#parameter buttons
Vreset=widgets.BoundedFloatText(
value=-1,
min=-100,
max=100,
step=0.1,
description='Voltage(V):',
)
#parameter buttons
CC_vreset=widgets.BoundedFloatText(
value=1e-3,
min=-0.1,
max=0.1,
step=0.01,
description='Comp(A):',
)
Vretention=widgets.BoundedFloatText(
value=1,
min=-100,
max=100,
step=1,
description='Voltage(V):',
)
period=widgets.BoundedFloatText(
value=1,
min=2e-3,
max=65.535,
step=1,
description='Period(s):',
)
duration=widgets.BoundedFloatText(
value=60,
min=60e-6,
max=1e11,
step=1,
description='Duration(s):',
)
#align a button with a checkbox or integer bounded texts horizontaly
line0=widgets.HBox([step,integration_time,sampling,auto_qcc])
line1 = widgets.HBox([set,Vset,CC_vset])
line2 = widgets.HBox([reset,Vreset,CC_vreset])
line3 = widgets.HBox([full,number])
line4 = widgets.HBox([retention_button,Vretention,period,duration])
#pack them into a single vertical box
all = widgets.VBox([line0,line1,line2,line3,line4])
output = widgets.Output()
#choose folder directory
folder=choose_folder()
#display all at the end
display(all_text_boxes)
cons_widgets,cons_dict = constant_pulse()
sweep_widgets,sweep_dict = sweep_pulse()
sweep_button = widgets.Button(description = "SWEEP PULSE")
cons_button = widgets.Button(description = "CONSTANT PULSE")
children = [all,widgets.VBox([sweep_widgets,sweep_button]),widgets.VBox([cons_widgets,cons_button])]
titles = ["Regular","Sweep Pulse","Constant Pulse"]
tab = widgets.Tab()
tab.children = children
tab.titles = titles
display(tab,output)
all_widgets=[sweep_button,cons_button,sample_series,field,DUT,set,reset,full,new_folder,retention_button,contact_check,qcc,qcc_select,Vset,CC_vset,Vreset,CC_vreset,step,integration_time,number,sampling,Vretention,period,duration,auto_qcc]
add_widgets_to_list(cons_dict,all_widgets)
add_widgets_to_list(sweep_dict,all_widgets)
device = hp4155a.HP4155a('GPIB0::17::INSTR')
device.reset()
device.disable_not_smu()
def on_contact_check_clicked(b):
global folder,temp_file
with output:
clear_output()
change_state(all_widgets)
device.inst.lock_excl()
filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt"
file = os.path.join(folder,filename)
R = regular_contact_check(device)
date = str(datetime.today().replace(microsecond=0))
title = [f"Full Contact Check ({qcc_select.value}) at {date}"]
write_to_file(temp_file,title,R)
#upload results
temp_file,file,folder=upload_results(temp_file,file,folder)
information_box("Contact Check Completed")
device.inst.unlock()
change_state(all_widgets)
def on_qcc_clicked(b):
global folder,temp_file
with output:
clear_output()
change_state(all_widgets)
device.inst.lock_excl()
filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt"
file = os.path.join(folder,filename)
device.inst.lock_excl()
if qcc_select.value == 'EBL':
R = EBL(device)
else: # OL
R = OL(device) #df
date = str(datetime.today().replace(microsecond=0))
title = [f"Quick Contact Check ({qcc_select.value}) at {date}"]
write_to_file(temp_file,title,R)
#upload results
temp_file,file,folder=upload_results(temp_file,file,folder)
information_box("Quick Contact Check Completed")
device.inst.unlock()
change_state(all_widgets)
def on_set_button_clicked(b):
global folder,temp_file
with output:
#disable buttons
change_state(all_widgets)
filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt"
file = os.path.join(folder,filename)
#lock the device
device.inst.lock_excl()
clear_output()
#check values
valid = check_values(step.value,Vset.value,Vreset.value)
if valid == True:
if sampling.value == True: #do sampling set before set process(100mV)
R_mean_before= sampling_check(-0.01,device)
#execute measurement,plot results and save them
V12,I12 = sweep(0,Vset.value,step.value,CC_vset.value,integration_time.value,device)
plot_sweep(V12,I12,'SET')
df = create_data_frame(V12,I12)
display(df)
if sampling.value == True: #do sampling set after set process(10mV)
R_mean_after = sampling_check(0.01,device)
date = str(datetime.today().replace(microsecond=0))
title = [f"SET Memristor at {date}",f"Set Voltage={Vset.value}V",f"current compliance={CC_vset.value}A"]
if sampling.value == True:
title.extend([f"R(Ohm) Before/After",f"{R_mean_before} {R_mean_after}"])
write_to_file(temp_file,title,df)
#upload results
temp_file,file,folder=upload_results(temp_file,file,folder)
#show messagebox
information_box("Measurement finished!")
#unlock device
device.inst.unlock()
change_state(all_widgets)
def on_reset_button_clicked(b):
global folder,temp_file
with output:
change_state(all_widgets)
filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt"
file = os.path.join(folder,filename)
#lock device
device.inst.lock_excl()
clear_output()
#check values
valid = check_values(step.value,Vset.value,Vreset.value)
if valid == True:
if sampling.value == True: #do sampling set before reset process(10mV)
R_mean_before = sampling_check(0.01,device)
#execute measurement,plot results and save them
V34,I34 = sweep(0,Vreset.value,step.value,CC_vreset.value,integration_time.value,device)
plot_sweep(V34,I34,'RESET')
df = create_data_frame(V34,I34)
display(df)
if sampling.value == True: #do sampling set after reset process(100mV)
R_mean_after = sampling_check(-0.01,device)
date = str(datetime.today().replace(microsecond=0))
title =[f"RESET Memristor at {date}",f"Reset Voltage={Vreset.value}V",f"current compliance={CC_vreset.value}A"]
if sampling.value == True:
title.extend([f"R(Ohm) Before/After",f"{R_mean_before} {R_mean_after}"])
write_to_file(temp_file,title,df)
#Quick Contact Check after reset Process
if auto_qcc.value == True:
if qcc_select.value == 'EBL':
R=EBL(device)
else: # OL
R=OL(device)
title = [f"Automatic Quick Contact Check({qcc_select.value}) after Reset"]
write_to_file(temp_file,title,R)
#upload results
temp_file,file,folder=upload_results(temp_file,file,folder)
#show messagebox
information_box("Measurement finished!")
#unlock device
device.inst.unlock()
change_state(all_widgets)
def on_full_button_clicked(b):
global folder,temp_file
with output:
change_state(all_widgets)
filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt"
file = os.path.join(folder,filename)
# lock device
device.inst.lock_excl()
clear_output()
#check values
valid = check_values(step.value,Vset.value,Vreset.value)
date = str(datetime.today().replace(microsecond=0))
if valid == True:
with open(temp_file,'a') as f:
header =[f"{number.value} full sweeps with parameters:",f"Set Voltage = {Vset.value}V",f"Current compliance set = {CC_vset.value}A",f"Reset Voltage = {Vreset.value}V",f"Current compliance reset = {CC_vreset.value}A"]
fig, (ax1, ax2) = plt.subplots(2,sharex=True,figsize=(8,6)) #the plots share the same x axis
fig.suptitle('FULL SWEEP')
ax1.set_title('Linear I')
ax1.set(xlabel='Voltage(V)',ylabel='Current(A)')
ax2.set_title('Logarithmic I')
ax2.set(xlabel='Voltage(V)',ylabel='Current(A)')
ax2.set_yscale('log')
stop = False
def break_loop():
nonlocal stop
stop = True
#help list with the resistances
resistances = []
add_hotkey("esc",break_loop)
#execute number of measurements
for i in range(number.value):#here it is easier to implement the sampling checks
clear_output(wait = True)
if i>0:
display(fig)
if sampling.value == True: #before set(100mv)
R_mean_init = sampling_check(-0.01,device)
resistances.append(R_mean_init)
V12,I12 = sweep(0,Vset.value,step.value,CC_vset.value,integration_time.value,device) #set
plot_sweep(V12,I12,f"SET Iteration {i+1}")
#after set/before reset
if sampling.value == True: #before set(10mv)
R_mean_set = sampling_check(0.01,device)
resistances.append(R_mean_set)
V34,I34 = sweep(0,Vreset.value,step.value,CC_vreset.value,integration_time.value,device) #reset
plot_sweep(V34,I34,f"RESET Iteration {i+1}")
#after reset
if sampling.value == True:#-0.1V
R_mean_reset = sampling_check(-0.01,device)
resistances.append(R_mean_reset)
#Quick Contact Check after reset Process
if auto_qcc.value == True:
if qcc_select.value == 'EBL':
R = EBL(device)
else: # OL
R = OL(device)
#butterfly curve
V=np.concatenate((V12,V34))
I=np.concatenate((I12,I34))
#create data frame and save to file
df = create_data_frame(V,I)
display(df)
if i == 0 :
header.extend([f"{i+1} Iteration"])
title = header.copy()
else:
title = [f"{i+1} Iteration"]
if sampling.value == True:
title.extend([f"R(Ohm) INIT/SET/RESET",f"{R_mean_init} {R_mean_set} {R_mean_reset}"])
write_to_file(temp_file,title,df)
if auto_qcc.value == True:
title= [f"Quick Contact Check({qcc_select.value}) after Reset"]
write_to_file(temp_file,title,R)
#plot results
ax1.plot(V,I)
ax2.plot(V,np.absolute(I))
fig.tight_layout()
#check for loop termination
if stop == True:
clear_output(wait= True)
time.sleep(2)
display(fig)
information_box("Endurance stopped after esc!")
f.write("endurance stopped!\n\n")
break
else:
clear_output(wait = True)
time.sleep(2)
display(fig)
information_box("Endurance completed!")
f.write("endurance completed!\n\n")
remove_hotkey('esc')
stop = False
#plot resistances if sampling value == True or len(resistances) !=0
if len(resistances)!=0:
indexes = np.arange(1,len(resistances)+1)
resistances = np.absolute(resistances)
fig, ax = plt.subplots()
fig.suptitle('Average Resistances from sampling checks')
ax.set(xlabel='Index',ylabel='Resistance(Ohm)',yscale='log')
ax.scatter(indexes,resistances)
display(fig)
#upload results
temp_file,file,folder=upload_results(temp_file,file,folder)
#unlock the device
device.inst.unlock()
change_state(all_widgets)
#new_folder clicked
def on_new_folder_button_clicked(b):
global folder
with output:
change_state(all_widgets)
folder = choose_folder() #choose new folder
change_state(all_widgets)
def on_retention_button_clicked(b):
global folder,temp_file
with output:
change_state(all_widgets)
device.inst.lock_excl()
clear_output()
filename=f"{sample_series.value}_{field.value}_{DUT.value}.txt"
file = os.path.join(folder,filename)
#execute measurement
t,R=retention(Vretention.value,period.value,duration.value,device)
plot_retention(t,R)
df=create_retention_data_frame(t,R)
date = str(datetime.today().replace(microsecond=0))
title =[f"Retention Memristor at {date}",f"Voltage={Vretention.value}V",f"period={period.value}s",f"duration={duration.value}s"]
write_to_file(temp_file,title,df)
#upload results
temp_file,file,folder=upload_results(temp_file,file,folder)
#show messagebox
information_box("Measurement finished!")
device.inst.unlock()
change_state(all_widgets)
def on_sweep_button_clicked(b):
with output:
clear_output()
change_state(all_widgets)
check_pulse(sweep_dict)
sample_dict= {
'series':sample_series,
'field':field,
'dut':DUT
}
times,values = sweep_meas(sweep_dict,device)
plot_sweep_pulse(values)
save_sweep(folder,sample_dict,values,times,sweep_dict)
change_state(all_widgets)
def on_constant_button_clicked(b):
with output:
global first
clear_output()
change_state(all_widgets)
check_pulse(sweep_dict)
sample_dict= {
'series':sample_series,
'field':field,
'dut':DUT
}
times,values = constant_meas(cons_dict,device)
plot_constant_pulse(values)
save_constant(folder,sample_dict,values,times,cons_dict)
change_state(all_widgets)
#link buttons to widgets (pulsed)
sweep_button.on_click(on_sweep_button_clicked)
cons_button.on_click(on_constant_button_clicked)
#link buttons with functions
set.on_click(on_set_button_clicked)
reset.on_click(on_reset_button_clicked)
full.on_click(on_full_button_clicked)
new_folder.on_click(on_new_folder_button_clicked)
retention_button.on_click(on_retention_button_clicked)
contact_check.on_click(on_contact_check_clicked)
qcc.on_click(on_qcc_clicked)
%% Cell type:code id:b913930d-b120-4e59-8a42-d9eecb526a61 tags:
``` python
%run memristor.py
```
%% Output
%% Cell type:code id:1047e606-d5cb-420b-892f-766226339854 tags:
``` python
```
hp4155/memristor (Version 4.0)/schematic.png

17 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment