aboutsummaryrefslogtreecommitdiffstats
path: root/sources/apps/QUAC-TRNG/scripts
diff options
context:
space:
mode:
authorAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
committerAtaberk <olgunataberk@gmail.com>2022-09-25 17:22:03 +0200
commitdc0b3db1b4f1895a07e5fe280ee3790e87f97b9f (patch)
treeb47203aa281bdd959def4451c84d310cd9cf2e12 /sources/apps/QUAC-TRNG/scripts
downloaddram-bender-dc0b3db1b4f1895a07e5fe280ee3790e87f97b9f.tar.gz
Initial commit
Diffstat (limited to 'sources/apps/QUAC-TRNG/scripts')
-rw-r--r--sources/apps/QUAC-TRNG/scripts/convert_ent.py32
-rw-r--r--sources/apps/QUAC-TRNG/scripts/ft200_rc/README.md21
-rw-r--r--sources/apps/QUAC-TRNG/scripts/ft200_rc/__init__.py0
-rw-r--r--sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/1189902393_ft200_lcd.pdfbin0 -> 14203337 bytes
-rw-r--r--sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/210.pdfbin0 -> 102947 bytes
-rw-r--r--sources/apps/QUAC-TRNG/scripts/ft200_rc/ft200.py182
-rwxr-xr-xsources/apps/QUAC-TRNG/scripts/ft200_rc/usb_dev_path.sh13
-rw-r--r--sources/apps/QUAC-TRNG/scripts/run_ent.py43
-rw-r--r--sources/apps/QUAC-TRNG/scripts/run_vn_nist.py125
-rw-r--r--sources/apps/QUAC-TRNG/scripts/sha_bitstreams.py87
-rw-r--r--sources/apps/QUAC-TRNG/scripts/top_ents.py35
11 files changed, 538 insertions, 0 deletions
diff --git a/sources/apps/QUAC-TRNG/scripts/convert_ent.py b/sources/apps/QUAC-TRNG/scripts/convert_ent.py
new file mode 100644
index 0000000..b2c66ea
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/convert_ent.py
@@ -0,0 +1,32 @@
+import os
+import argparse
+from numpy import array
+
+parser = argparse.ArgumentParser(description = "Run this script to preprocess entropy files")
+parser.add_argument('indir', help="Path to entropy files")
+args = parser.parse_args()
+
+files = []
+for fname in os.listdir(args.indir):
+ files.append(fname)
+
+for file in files:
+ print("Processing:", file)
+ # Add this placement
+ fpath = os.path.normpath(args.indir + '/' + file)
+ infile = open(fpath,'r')
+ wfile = file.split('.')[0] + '_floats.bin'
+ wfpath = os.path.normpath(args.indir + '/' + wfile)
+ output_file = open(wfpath, 'wb')
+
+ lines = infile.readlines()
+
+ # First row reserved for metadata
+ for lidx in range(1,len(lines)):
+ line = lines[lidx].strip()
+ split = line.split()
+ elements = [float(i) for i in split[1:]]
+ a = array(elements,'float32')
+ a.tofile(output_file)
+
+ output_file.close() \ No newline at end of file
diff --git a/sources/apps/QUAC-TRNG/scripts/ft200_rc/README.md b/sources/apps/QUAC-TRNG/scripts/ft200_rc/README.md
new file mode 100644
index 0000000..de91b27
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/ft200_rc/README.md
@@ -0,0 +1,21 @@
+# FT200 remote read and write
+
+Make sure that the FT200 controller match the parameters you are using in your script.
+The relevant parameters are:
+
+PASS-0101 menu (see documentation in doc folder):
+* ldN0 (Device address)
+* bAUd (rate)
+* UCR (parity)
+
+## Requirements
+* pip3 install -U minimalmodbus
+
+
+## Add your user to the dialout group
+
+sudo gpasswd --add ${USER} dialout
+(You need to log out and log back in again for it to be effective)
+
+## Execute test
+* python3 ft200.py
diff --git a/sources/apps/QUAC-TRNG/scripts/ft200_rc/__init__.py b/sources/apps/QUAC-TRNG/scripts/ft200_rc/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/ft200_rc/__init__.py
diff --git a/sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/1189902393_ft200_lcd.pdf b/sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/1189902393_ft200_lcd.pdf
new file mode 100644
index 0000000..8c18eaa
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/1189902393_ft200_lcd.pdf
Binary files differ
diff --git a/sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/210.pdf b/sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/210.pdf
new file mode 100644
index 0000000..e4d0d39
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/ft200_rc/docs/210.pdf
Binary files differ
diff --git a/sources/apps/QUAC-TRNG/scripts/ft200_rc/ft200.py b/sources/apps/QUAC-TRNG/scripts/ft200_rc/ft200.py
new file mode 100644
index 0000000..aa50a1f
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/ft200_rc/ft200.py
@@ -0,0 +1,182 @@
+"""""
+Code for monitoring and controlling temperature in the Maxwell FT200 controller using the Modbus RS485 protocol
+@author: Lois Orosa
+"""""
+import minimalmodbus as mm, serial, time
+import time
+import os
+import sys
+import pathlib
+
+class FT200:
+ """
+ Controls and monitors the Maxwell FT200 tempoerature controller
+ """
+ READ_ERROR = -999
+ WRITE_ERROR = -998
+ def __init__(self, _baud=9600, _ft200_addr=1, _temp_tolerance=2):
+ self.baud = _baud
+ self.write_reg = 5 # Write register
+ self.read_reg = 0 # Read register
+ self.ft200_addr = _ft200_addr # Address of the device (by default is 1)
+ self.temp_tolerance = _temp_tolerance # Temperature tolerance. By default, we consider that we reach the target value if the measured value is within +-0.2C
+
+ try:
+ self.device = self.findDevice() # Get the USB-RS485 device
+ print("USB-RS485 Device: "+str(self.device))
+ self.instrument = mm.Instrument(self.device , 3 , debug = False)
+ self.instrument.serial.baudrate = self.baud
+ self.instrument.address = self.ft200_addr
+ self.instrument.serial.timeout = 1
+ self.instrument.serial.parity = serial.PARITY_NONE
+ self.instrument.mode = mm.MODE_RTU
+ print("Device "+self.device+": conected")
+ except:
+ raise
+
+ # Automatically find the USB-RS485 Device
+ def findDevice(self):
+ '''
+ Find the USB-RS485 device
+ This function might raise two exceptions:
+ -> NameError: There is more than 1 USB-RS485 device, or other USB device has a similar name as the USB-RS485 device
+ -> IOError: Device not found.
+ '''
+ DEVICENAME = "1a86_USB"
+ name = ''
+ while True:
+ path = pathlib.Path(__file__).parent.absolute()
+ f = os.popen(str(path)+"/usb_dev_path.sh")
+ for device in f:
+ if DEVICENAME in device:
+ if name == '':
+ name = device.split()[0]
+ else:
+ raise NameError("There are at least two USB devices whose name contains "+str(DEVICENAME))
+ if name == '':
+ raise IOError("Device not found.")
+ return name
+
+ # Read Temperature from FT200
+ # The output value has 1 decimal, and it is multiplied by 10
+ # E.g., data= 102 represents a temperature of 10.2C
+ def readTemp(self):
+ try:
+ data = self.instrument.read_register(self.read_reg, functioncode=3)
+ return data
+ except IOError:
+ print("Failed to read from instrument")
+ return self.READ_ERROR
+
+ # Check if the temperature reached a value
+ # The input value has 1 decimal, and it is multiplied by 10
+ # E.g., data= 102 represents a temperature of 10.2C
+ def isTemp(self, temperature):
+ try:
+ data = self.instrument.read_register(self.read_reg, functioncode=3)
+ if data >= (temperature-self.temp_tolerance) and data <= (temperature+self.temp_tolerance):
+ return True
+ else:
+ return False
+ except IOError:
+ print("Failed to read from instrument")
+ return False
+
+ # Set Temperature FT200
+ # The value should be a integer, and it should be the temperature value with 1 decimal multiplied by 10
+ # E.g., value= 102 represents a temperature of 10.2C
+ def setTemp(self, value):
+ try:
+ self.instrument.write_register(self.write_reg, value, functioncode=6)
+ except IOError:
+ print("Failed to write to instrument")
+ return self.WRITE_ERROR
+ return 0
+
+ # Set the temperature and wait for the temperature to be stable
+ # Optimized version that reach the target temperature earlier
+ # The value should be a integer, and it should be the temperature value with 1 decimal multiplied by 10
+ # E.g., value= 102 represents a temperature of 10.2C
+ def autoSetAndWait(self, temperature, debug = False):
+ acceleration_factor = 5
+ stability = 10 # Iterations that the temperature needs to be stable
+ time_sleep = 1 # 1 second wait per loop iteration
+ stable_count = stability # Counter for taking into account the stability of the temperature
+ stable_margin = 20 # We do not change the set temperature if we are within 2C from the target
+ optTemp = temperature
+ self.setTemp(optTemp) # Set the temperature
+ curTemp = self.readTemp() # Read the temperature
+ if debug:
+ print("[TEMPERATURE] current: "+str(curTemp/10.0)+", goal: "+str(temperature/10.0)+", programmed: "+str(optTemp/10.0))
+ while (((curTemp > (temperature + self.temp_tolerance)) or (curTemp < (temperature - self.temp_tolerance))) or (stable_count!=0)):
+ if debug:
+ print("[TEMPERATURE] current: "+str(curTemp/10.0)+", goal: "+str(temperature/10.0)+", programmed: "+str(optTemp/10.0)+", stable_count: "+str(stable_count))
+ prev_optTemp = optTemp
+ if (temperature - curTemp) > 0: #(stable_margin): # If we are far away more than 2C
+ optTemp = temperature + (temperature - curTemp)*acceleration_factor # Optimization to accelerate convergence
+ else:
+ optTemp = temperature
+ if optTemp != prev_optTemp:
+ self.setTemp(optTemp) # Set temperature only if different from the previous iteration
+
+ time.sleep(time_sleep) # WAit for a while
+
+ curTemp = self.readTemp() # Read the temperature
+ while (curTemp == self.READ_ERROR): # If there is an error, read again
+ curTemp = self.readTemp()
+ # Five iterations in the range
+ if (curTemp > (temperature + self.temp_tolerance)) or (curTemp < (temperature - self.temp_tolerance)):
+ stable_count = stability
+ else:
+ stable_count -= 1 # The temperature has to remain stable for a while
+
+ def autoSetAndWait_2(self, temperature, debug = False):
+ acceleration_factor = 150
+ stability = 10 # Iterations that the temperature needs to be stable
+ time_sleep = 1 # 1 second wait per loop iteration
+ stable_count = stability # Counter for taking into account the stability of the temperature
+ stable_margin = 5 # We do not change the set temperature if we are within 2C from the target
+ optTemp = temperature
+ self.setTemp(optTemp) # Set the temperature
+ curTemp = self.readTemp() # Read the temperature
+ if debug:
+ print("[TEMPERATURE] current: "+str(curTemp/10.0)+", goal: "+str(temperature/10.0)+", programmed: "+str(optTemp/10.0))
+ while (((curTemp > (temperature + self.temp_tolerance)) or (curTemp < (temperature - self.temp_tolerance))) or (stable_count!=0)):
+ if debug:
+ print("[TEMPERATURE] current: "+str(curTemp/10.0)+", goal: "+str(temperature/10.0)+", programmed: "+str(optTemp/10.0)+", stable_count: "+str(stable_count))
+ prev_optTemp = optTemp
+ if abs(temperature - curTemp) > (stable_margin): # If we are far away more than 2C
+ if temperature > curTemp:
+ optTemp = temperature + acceleration_factor # Optimization to accelerate convergence
+ else:
+ optTemp = temperature - acceleration_factor # Optimization to accelerate convergence
+ else:
+ optTemp = temperature
+
+ time.sleep(time_sleep) # WAit for a while
+
+ curTemp = self.readTemp() # Read the temperature
+ while (curTemp == self.READ_ERROR): # If there is an error, read again
+ curTemp = self.readTemp()
+ # Five iterations in the range
+ if (curTemp > (temperature + self.temp_tolerance)) or (curTemp < (temperature - self.temp_tolerance)):
+ stable_count = stability
+ else:
+ stable_count -= 1 # The temperature has to remain stable for a while
+
+if __name__ == '__main__':
+ ## Example of how to use the FT200 class
+ try:
+ tc = FT200()
+ except Exception as error:
+ print("[ERROR] "+ repr(error))
+ sys.exit(0)
+
+ # Temperature
+ value = int(sys.argv[1]) * 10
+ print(value)
+ print(tc.readTemp())
+ tc.autoSetAndWait(value,True)
+
+
+
diff --git a/sources/apps/QUAC-TRNG/scripts/ft200_rc/usb_dev_path.sh b/sources/apps/QUAC-TRNG/scripts/ft200_rc/usb_dev_path.sh
new file mode 100755
index 0000000..241aef1
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/ft200_rc/usb_dev_path.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+# https://unix.stackexchange.com/questions/144029/command-to-determine-ports-of-a-device-like-dev-ttyusb0
+echo "USB devices:"
+for sysdevpath in $(find /sys/bus/usb/devices/usb*/ -name dev); do
+ (
+ syspath="${sysdevpath%/dev}"
+ devname="$(udevadm info -q name -p $syspath)"
+[[ "$devname" == "bus/"* ]] && exit
+ eval "$(udevadm info -q property --export -p $syspath)"
+[[ -z "$ID_SERIAL" ]] && exit
+ echo "/dev/$devname - $ID_SERIAL"
+ )
+done
diff --git a/sources/apps/QUAC-TRNG/scripts/run_ent.py b/sources/apps/QUAC-TRNG/scripts/run_ent.py
new file mode 100644
index 0000000..5163c5f
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/run_ent.py
@@ -0,0 +1,43 @@
+import os
+import argparse
+import subprocess
+import time
+
+parser = argparse.ArgumentParser(description = "Collects entropy for different data patterns")
+
+parser.add_argument('dimm', help="DIMM label under test")
+parser.add_argument('temperature', help="DIMM temperature in centigrades")
+parser.add_argument('iters', help="How many times do we QUAC to find entropy")
+parser.add_argument('stride', help="Stride (in rows) used to skim through segments, default: 4")
+
+args = parser.parse_args()
+
+# ---------------- SETUP DIRECTORIES -------------------
+
+if not os.path.isdir("data"):
+ os.mkdir("data")
+
+if not os.path.isdir("data/" + args.dimm):
+ os.mkdir("data/" + args.dimm)
+
+if not os.path.isdir("data/" + args.dimm + "/ent"):
+ os.mkdir("data/" + args.dimm + "/ent")
+
+if not os.path.isdir("data/" + args.dimm + "/ent/" + args.temperature + "C"):
+ os.mkdir("data/" + args.dimm + "/ent/" + args.temperature + "C")
+
+outdir = os.path.normpath("data/" + args.dimm + "/ent/" + args.temperature + "C")
+
+# -------------------- RUN TESTS -----------------------
+
+placements = ["1000", "1001", "1010", "1011", "1100" , "1101", "1110", "1111", \
+ "0111", "0110", "0101", "0100", "0011" , "0010", "0001", "0000"]
+
+ent_path = os.path.normpath("bin/ENT")
+results_path = outdir
+stride = args.stride
+iters = args.iters
+
+for placement in placements:
+ print("sudo " + ent_path + " " + results_path + " " + placement + " " + stride + " " + iters)
+ os.system("sudo " + ent_path + " " + results_path + " " + placement + " " + stride + " " + iters)
diff --git a/sources/apps/QUAC-TRNG/scripts/run_vn_nist.py b/sources/apps/QUAC-TRNG/scripts/run_vn_nist.py
new file mode 100644
index 0000000..c439da8
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/run_vn_nist.py
@@ -0,0 +1,125 @@
+import os
+import argparse
+import subprocess
+import time
+
+parser = argparse.ArgumentParser(description = "Run this script to evaluate bitstreams")
+
+parser.add_argument('dimm', help="DIMM label under test")
+parser.add_argument('temperature', help="DIMM temperature in centigrades")
+
+args = parser.parse_args()
+
+# ---------------- SETUP DIRECTORIES -------------------
+
+if not os.path.isdir("data"):
+ os.mkdir("data")
+
+if not os.path.isdir("data/" + args.dimm):
+ os.mkdir("data/" + args.dimm)
+
+if not os.path.isdir("data/" + args.dimm + "/vnc"):
+ os.mkdir("data/" + args.dimm + "/vnc")
+
+if not os.path.isdir("data/" + args.dimm + "/vnc/" + args.temperature + "C"):
+ os.mkdir("data/" + args.dimm + "/vnc/" + args.temperature + "C")
+
+if not os.path.isdir("data/" + args.dimm + "/vnc/" +
+ args.temperature + "C/nist_results"):
+ os.mkdir("data/" + args.dimm + "/vnc/" + args.temperature + "C/nist_results")
+
+if not os.path.isdir("data/" + args.dimm + "/vnc/" +
+ args.temperature + "C/binaries"):
+ os.mkdir("data/" + args.dimm + "/vnc/" + args.temperature + "C/binaries")
+
+outdir = os.path.normpath("data/" + args.dimm + "/vnc/" + args.temperature + "C/nist_results")
+indir = os.path.normpath("data/" + args.dimm + "/vnc/" + args.temperature + "C/binaries")
+
+if not os.path.isfile(indir + "/lastrow.txt"):
+ input("I am going to overwrite 'lastrow.txt', proceed with caution")
+ init_file = open(indir + "/lastrow.txt", "w")
+ init_file.write(str(0) + "\n")
+
+# Hard-reset SoftMC before other experiments
+reset_path = os.path.normpath("bin/SoftMC_reset")
+reset = subprocess.Popen("exec sudo " + reset_path, shell=True, preexec_fn=os.setpgrp)
+time.sleep(5)
+os.system("sudo pkill -9 -P" + str(os.getpgid(reset.pid)))
+reset.wait()
+
+# First run OBSERVE to collect von Neumann corrected bitstreams
+observe_path = os.path.normpath("bin/OBSERVE " + indir)
+observe_args = " > " + os.path.normpath(outdir + "/output.log")
+observe = subprocess.Popen("exec sudo " + observe_path + observe_args, shell=True, preexec_fn=os.setpgrp)
+
+# Watch the bitstream directory for new files
+before = ["lastrow.txt"]
+
+while True:
+ time.sleep(1)
+ after = os.listdir(indir)
+ s = set(before)
+ diff = [x for x in after if x not in s]
+ before = after
+ if len(diff) == 0:
+ continue
+
+ bitstream = [[] for i in range(8)] # at most 8 STS runs
+
+ # Partition
+ i = 0
+ for fname in diff:
+ bitstream[i].append(fname)
+ i = (i+1) % 8
+
+ found = False
+ for i in range(len(bitstream[0])):
+ print("Finished " + str(i) + " out of " + str(len(bitstream[7])))
+ processes = []
+ for thr in range(0,8):
+ if len(bitstream[thr]) <= i:
+ break
+ command_output = os.path.normpath(outdir + "/thr" + str(thr))
+ command_input = os.path.normpath(indir + '/' + bitstream[thr][i])
+ command = 'tools/sts -O -i 1 -I 1 -S '+ str(1024*1024) +' -w ' + command_output +' -F r ' + command_input + " 2>/dev/null"
+ process = subprocess.Popen(command, shell=True)
+ processes.append(process)
+
+ # Collect statuses
+ output = [p.wait() for p in processes]
+
+ for thr in range(0,8):
+ if len(bitstream[thr]) <= i:
+ break
+ res_txt = open(os.path.normpath(outdir + '/thr' + str(thr) + '/finalAnalysisReport.txt'), "r")
+ lines = res_txt.readlines()
+ n_success = 0
+ for j in range(7, len(lines)):
+ line = lines[j].strip()
+ if not ("1" in line or "0" in line):
+ break
+ split = line.split()
+ if "/" in split[11]:
+ n_success += int(split[11].split("/")[0])
+ res_txt.close()
+ print(bitstream[thr][i] + " succeeded in " + str(n_success) + "tests + bits: " + str(1024*1024))
+ if n_success < 187:
+ os.system("rm -f " + indir + "/" + bitstream[thr][i])
+ if n_success > 186:
+ os.system("mkdir " + outdir + "/" + bitstream[thr][i])
+ os.system("cp " + outdir + '/thr' + str(thr) + "/finalAnalysisReport.txt " + outdir + "/" + bitstream[thr][i])
+ if n_success == 188:
+ os.system("sudo pkill -9 -P" + str(os.getpgid(observe.pid)))
+ observe.wait() # wait until the process actually terminates
+ found = True
+ if found:
+ break
+
+print("Finally found one cell that passes all NIST tests!")
+
+# Reset SoftMC because last process was killed prematurely
+reset_path = os.path.normpath("bin/SoftMC_reset")
+reset = subprocess.Popen("exec sudo " + observe_path, shell=True, preexec_fn=os.setpgrp)
+time.sleep(5)
+os.system("sudo pkill -9 -P" + str(os.getpgid(reset.pid)))
+reset.wait()
diff --git a/sources/apps/QUAC-TRNG/scripts/sha_bitstreams.py b/sources/apps/QUAC-TRNG/scripts/sha_bitstreams.py
new file mode 100644
index 0000000..fd4dbfb
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/sha_bitstreams.py
@@ -0,0 +1,87 @@
+import os
+import argparse
+import subprocess
+import time
+
+parser = argparse.ArgumentParser(description = "Collects sha bitstreams for a data pattern")
+
+parser.add_argument('dimm', help="DIMM label under test")
+parser.add_argument('pattern', help="data pattern to use")
+parser.add_argument('temperature', help="DIMM temperature in centigrades")
+
+args = parser.parse_args()
+
+bitstreams_available = False
+
+if not os.path.isdir("data/" + args.dimm + "/sha"):
+ os.mkdir("data/" + args.dimm + "/sha")
+
+if os.path.isdir("data/" + args.dimm + "/sha/" + args.temperature + "C"):
+ bitstreams_available = True
+
+if not os.path.isdir("data/" + args.dimm + "/sha/" + args.temperature + "C"):
+ os.mkdir("data/" + args.dimm + "/sha/" + args.temperature + "C")
+
+infile = os.path.normpath("data/" + args.dimm + "/ent/" + args.temperature + "C/top.txt")
+outdir = os.path.normpath("data/" + args.dimm + "/sha/" + args.temperature + "C")
+
+if not os.path.isdir("data/" + args.dimm + "/sha"):
+ os.mkdir("data/" + args.dimm + "/sha_nist_results")
+
+if not os.path.isdir("data/" + args.dimm + "/sha/" + args.temperature + "C"):
+ os.mkdir("data/" + args.dimm + "/sha_nist_results/" + args.temperature + "C")
+
+sha_output = os.path.normpath("data/" + args.dimm + "/sha_nist_results/" + args.temperature + "C")
+
+if not bitstreams_available:
+ sha_path = "bin/SHA"
+ print("sudo " + sha_path + " " + outdir + " " + infile)
+ os.system("sudo " + sha_path + " " + outdir + " " + infile)
+else:
+ print("I think we have the bitstreams available, I won't run softmc code again")
+
+i=0
+bitstream = []
+
+for j in range(0,10):
+ bitstream.append([])
+
+for fname in os.listdir(outdir):
+ bitstream[i].append(fname)
+ i+=1
+ i = i % 10
+
+print(bitstream)
+
+for i in range(len(bitstream[9])):
+ print("Finished " + str(i) + " out of " + str(len(bitstream[9])))
+ processes = []
+ for thr in range(0,10):
+ outpath = os.path.normpath(sha_output+'/thr'+str(thr))
+ if not os.path.isdir(outpath):
+ os.mkdir(outpath)
+ command = 'tools/sts -O -i 1024 -I 1 -P 11=0.001 -S '+ str(1024*1024) +' -w '+ outpath +' -F r ' + outdir+'/'+bitstream[thr][i] + " 2>/dev/null"
+ process = subprocess.Popen(command, shell=True)
+ processes.append(process)
+ print(command)
+
+ # Collect statuses
+ output = [p.wait() for p in processes]
+
+ for thr in range(0,10):
+ outpath = os.path.normpath(sha_output+'/thr'+str(thr))
+ res_txt = open(outpath + '/finalAnalysisReport.txt', "r")
+ lines = res_txt.readlines()
+ n_success = 0
+ for j in range(7, len(lines)):
+ line = lines[j].strip()
+ if not ("1" in line or "0" in line):
+ break
+ split = line.split()
+ if "/" in split[11]:
+ n_success += int(split[11].split("/")[0])
+ res_txt.close()
+ print(bitstream[thr][i] + " succeeded in " + str(n_success) + "tests + bits: " + str(1024*1024))
+ if n_success > 186:
+ os.system("mkdir "+sha_output+"/" + bitstream[thr][i])
+ os.system("cp "+sha_output+'/thr'+str(thr)+"/finalAnalysisReport.txt "+sha_output+"/" + bitstream[thr][i])
diff --git a/sources/apps/QUAC-TRNG/scripts/top_ents.py b/sources/apps/QUAC-TRNG/scripts/top_ents.py
new file mode 100644
index 0000000..2a74569
--- /dev/null
+++ b/sources/apps/QUAC-TRNG/scripts/top_ents.py
@@ -0,0 +1,35 @@
+import os
+import argparse
+import pandas as pd
+import numpy as np
+
+parser = argparse.ArgumentParser(description = "Postprocess entropy files and obtain top-10 segments with the highest entropy")
+parser.add_argument('dimm', help="DIMM label")
+parser.add_argument('pattern', help="4-bit data pattern [1000, 1001, 0111, etc]")
+parser.add_argument('temp', help="temperature in centigrades")
+args = parser.parse_args()
+
+dimm = args.dimm
+pattern = args.pattern
+temp = args.temp
+
+no_segments = 8192
+no_bitlines = 512 * 128
+
+fn = "data/" + dimm + "/ent/" + temp + "C/1_1000_128_32768_" + pattern + "_floats.bin"
+dirname = "data/" + dimm + "/ent/" + temp + "C/"
+
+arr = np.fromfile(fn, dtype=np.float32)
+arr = arr.reshape(no_segments, no_bitlines)
+arr = arr.sum(axis = 1)
+indices = arr.argsort()[-10:][::-1]
+print(indices)
+print(arr[indices])
+
+log = open(dirname + "top.txt", "w")
+log.write("10\n")
+for index in indices:
+ # bank is always 1
+ # multiply segment # by 4 to get starting row addr
+ log.write("1 " + str(int(index) * 4) + " " + str(arr[index]) + " " + pattern + "\n")
+log.close()